Replace libdbus with qdbus
This commit is contained in:
parent
590c81cf84
commit
2170cfe78e
2 changed files with 57 additions and 233 deletions
|
@ -12,6 +12,9 @@
|
|||
#include <QFile>
|
||||
#include <QIODevice>
|
||||
#include <QImage>
|
||||
#include <QDBusMessage>
|
||||
#include <QDBusConnection>
|
||||
#include <QDBusConnectionInterface>
|
||||
|
||||
#include <core/kdeconnectplugin.h>
|
||||
#include <core/kdeconnectpluginconfig.h>
|
||||
|
@ -23,236 +26,73 @@
|
|||
#include "plugin_sendnotifications_debug.h"
|
||||
#include "sendnotificationsplugin.h"
|
||||
|
||||
const char *NOTIFY_SIGNATURE = "susssasa{sv}i";
|
||||
|
||||
QString becomeMonitor(DBusConnection *conn, const char *match)
|
||||
void NotificationsListener::handleMessage(const QDBusMessage& message)
|
||||
{
|
||||
// message
|
||||
DBusMessage *msg = dbus_message_new_method_call(DBUS_SERVICE_DBUS, DBUS_PATH_DBUS, DBUS_INTERFACE_MONITORING, "BecomeMonitor");
|
||||
Q_ASSERT(msg != nullptr);
|
||||
qWarning() << message;
|
||||
|
||||
// arguments
|
||||
const char *matches[] = {match};
|
||||
const char **matches_ = matches;
|
||||
dbus_uint32_t flags = 0;
|
||||
|
||||
bool success = dbus_message_append_args(msg, DBUS_TYPE_ARRAY, DBUS_TYPE_STRING, &matches_, 1, DBUS_TYPE_UINT32, &flags, DBUS_TYPE_INVALID);
|
||||
if (!success) {
|
||||
dbus_message_unref(msg);
|
||||
return QStringLiteral("Failed to call dbus_message_append_args");
|
||||
}
|
||||
|
||||
// send
|
||||
// TODO: wait and check for error: dbus_connection_send_with_reply_and_block
|
||||
success = dbus_connection_send(conn, msg, nullptr);
|
||||
if (!success) {
|
||||
dbus_message_unref(msg);
|
||||
return QStringLiteral("Failed to call dbus_connection_send");
|
||||
}
|
||||
|
||||
dbus_message_unref(msg);
|
||||
|
||||
return QString();
|
||||
}
|
||||
|
||||
extern "C" DBusHandlerResult handleMessageFromC(DBusConnection *, DBusMessage *message, void *user_data)
|
||||
{
|
||||
auto *self = static_cast<NotificationsListenerThread *>(user_data);
|
||||
if (dbus_message_is_method_call(message, "org.freedesktop.Notifications", "Notify")) {
|
||||
self->handleNotifyCall(message);
|
||||
}
|
||||
// Monitors must not allow libdbus to reply to messages, so we eat the message.
|
||||
return DBUS_HANDLER_RESULT_HANDLED;
|
||||
}
|
||||
|
||||
void NotificationsListenerThread::stop()
|
||||
{
|
||||
if (m_connection) {
|
||||
dbus_connection_close(m_connection);
|
||||
dbus_connection_unref(m_connection);
|
||||
m_connection = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void NotificationsListenerThread::run()
|
||||
{
|
||||
DBusError err = DBUS_ERROR_INIT;
|
||||
m_connection = dbus_bus_get_private(DBUS_BUS_SESSION, &err);
|
||||
if (dbus_error_is_set(&err)) {
|
||||
qCWarning(KDECONNECT_PLUGIN_SENDNOTIFICATIONS) << "D-Bus connection failed" << err.message;
|
||||
dbus_error_free(&err);
|
||||
if (message.interface() != QStringLiteral("org.freedesktop.Notifications") || message.member() != QStringLiteral("Notify")) {
|
||||
qWarning() << "Wrong method";
|
||||
return;
|
||||
}
|
||||
|
||||
Q_ASSERT(m_connection != nullptr);
|
||||
const QString notifySignature = QStringLiteral("susssasa{sv}i");
|
||||
|
||||
dbus_connection_set_route_peer_messages(m_connection, true);
|
||||
dbus_connection_set_exit_on_disconnect(m_connection, false);
|
||||
dbus_connection_add_filter(m_connection, handleMessageFromC, this, nullptr);
|
||||
|
||||
QString error = becomeMonitor(m_connection,
|
||||
"interface='org.freedesktop.Notifications',"
|
||||
"member='Notify'");
|
||||
|
||||
if (!error.isEmpty()) {
|
||||
qCWarning(KDECONNECT_PLUGIN_SENDNOTIFICATIONS).noquote() << "Failed to become a DBus monitor."
|
||||
<< "No notifictions will be sent. Error:" << error;
|
||||
if (message.signature() != notifySignature) {
|
||||
qWarning() << "Wrong signature";
|
||||
}
|
||||
|
||||
// wake up every minute to see if we are still connected
|
||||
while (m_connection != nullptr) {
|
||||
dbus_connection_read_write_dispatch(m_connection, 60 * 1000);
|
||||
}
|
||||
|
||||
deleteLater();
|
||||
}
|
||||
|
||||
static unsigned nextUnsigned(DBusMessageIter *iter)
|
||||
{
|
||||
Q_ASSERT(dbus_message_iter_get_arg_type(iter) == DBUS_TYPE_UINT32);
|
||||
DBusBasicValue value;
|
||||
dbus_message_iter_get_basic(iter, &value);
|
||||
dbus_message_iter_next(iter);
|
||||
return value.u32;
|
||||
}
|
||||
|
||||
static int nextInt(DBusMessageIter *iter)
|
||||
{
|
||||
Q_ASSERT(dbus_message_iter_get_arg_type(iter) == DBUS_TYPE_INT32);
|
||||
DBusBasicValue value;
|
||||
dbus_message_iter_get_basic(iter, &value);
|
||||
dbus_message_iter_next(iter);
|
||||
return value.i32;
|
||||
}
|
||||
|
||||
static QString nextString(DBusMessageIter *iter)
|
||||
{
|
||||
Q_ASSERT(dbus_message_iter_get_arg_type(iter) == DBUS_TYPE_STRING);
|
||||
DBusBasicValue value;
|
||||
dbus_message_iter_get_basic(iter, &value);
|
||||
dbus_message_iter_next(iter);
|
||||
return QString::fromUtf8(value.str);
|
||||
}
|
||||
|
||||
static QStringList nextStringList(DBusMessageIter *iter)
|
||||
{
|
||||
DBusMessageIter sub;
|
||||
dbus_message_iter_recurse(iter, &sub);
|
||||
dbus_message_iter_next(iter);
|
||||
|
||||
QStringList list;
|
||||
while (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_INVALID) {
|
||||
list.append(nextString(&sub));
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
static QVariant nextVariant(DBusMessageIter *iter)
|
||||
{
|
||||
int type = dbus_message_iter_get_arg_type(iter);
|
||||
if (type != DBUS_TYPE_VARIANT)
|
||||
return QVariant();
|
||||
|
||||
DBusMessageIter sub;
|
||||
dbus_message_iter_recurse(iter, &sub);
|
||||
dbus_message_iter_next(iter);
|
||||
|
||||
type = dbus_message_iter_get_arg_type(&sub);
|
||||
if (dbus_type_is_basic(type)) {
|
||||
DBusBasicValue value;
|
||||
dbus_message_iter_get_basic(&sub, &value);
|
||||
switch (type) {
|
||||
case DBUS_TYPE_BOOLEAN:
|
||||
return QVariant(value.bool_val);
|
||||
case DBUS_TYPE_INT16:
|
||||
return QVariant(value.i16);
|
||||
case DBUS_TYPE_INT32:
|
||||
return QVariant(value.i32);
|
||||
case DBUS_TYPE_INT64:
|
||||
return QVariant((qlonglong)value.i64);
|
||||
case DBUS_TYPE_UINT16:
|
||||
return QVariant(value.u16);
|
||||
case DBUS_TYPE_UINT32:
|
||||
return QVariant(value.u32);
|
||||
case DBUS_TYPE_UINT64:
|
||||
return QVariant((qulonglong)value.u64);
|
||||
case DBUS_TYPE_BYTE:
|
||||
return QVariant(value.byt);
|
||||
case DBUS_TYPE_DOUBLE:
|
||||
return QVariant(value.dbl);
|
||||
case DBUS_TYPE_STRING:
|
||||
return QVariant(QString::fromUtf8(value.str));
|
||||
}
|
||||
}
|
||||
|
||||
qCWarning(KDECONNECT_PLUGIN_SENDNOTIFICATIONS) << "Unimplemented conversation of type" << QChar(type) << type;
|
||||
|
||||
return QVariant();
|
||||
}
|
||||
|
||||
static QVariantMap nextVariantMap(DBusMessageIter *iter)
|
||||
{
|
||||
DBusMessageIter sub;
|
||||
dbus_message_iter_recurse(iter, &sub);
|
||||
dbus_message_iter_next(iter);
|
||||
|
||||
QVariantMap map;
|
||||
while (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_INVALID) {
|
||||
DBusMessageIter entry;
|
||||
dbus_message_iter_recurse(&sub, &entry);
|
||||
dbus_message_iter_next(&sub);
|
||||
QString key = nextString(&entry);
|
||||
QVariant value = nextVariant(&entry);
|
||||
map.insert(key, value);
|
||||
}
|
||||
return map;
|
||||
}
|
||||
|
||||
void NotificationsListenerThread::handleNotifyCall(DBusMessage *message)
|
||||
{
|
||||
DBusMessageIter iter;
|
||||
dbus_message_iter_init(message, &iter);
|
||||
|
||||
if (!dbus_message_has_signature(message, NOTIFY_SIGNATURE)) {
|
||||
qCWarning(KDECONNECT_PLUGIN_SENDNOTIFICATIONS).nospace()
|
||||
<< "Call to Notify has wrong signature. Expected " << NOTIFY_SIGNATURE << ", got " << dbus_message_get_signature(message);
|
||||
QList<QVariant> args = message.arguments();
|
||||
if (args.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
QString appName = nextString(&iter);
|
||||
uint replacesId = nextUnsigned(&iter);
|
||||
QString appIcon = nextString(&iter);
|
||||
QString summary = nextString(&iter);
|
||||
QString body = nextString(&iter);
|
||||
QStringList actions = nextStringList(&iter);
|
||||
QVariantMap hints = nextVariantMap(&iter);
|
||||
int timeout = nextInt(&iter);
|
||||
QString appName = args.at(0).toString();
|
||||
uint replacesId = args.at(1).toUInt();
|
||||
QString appIcon = args.at(2).toString();
|
||||
QString summary = args.at(3).toString();
|
||||
QString body = args.at(4).toString();
|
||||
QStringList actions = args.at(5).toStringList();
|
||||
QVariantMap hints = args.at(6).toMap();
|
||||
int timeout = args.at(7).toInt();
|
||||
|
||||
Q_EMIT notificationReceived(appName, replacesId, appIcon, summary, body, actions, hints, timeout);
|
||||
handleNotification(appName, replacesId, appIcon, summary, body, actions, hints, timeout);
|
||||
}
|
||||
|
||||
NotificationsListener::NotificationsListener(KdeConnectPlugin *aPlugin)
|
||||
: QObject(aPlugin)
|
||||
, m_plugin(aPlugin)
|
||||
, m_thread(new NotificationsListenerThread())
|
||||
, sessionBus(QDBusConnection::sessionBus())
|
||||
{
|
||||
#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
|
||||
qRegisterMetaTypeStreamOperators<NotifyingApplication>("NotifyingApplication");
|
||||
#endif
|
||||
|
||||
if (!sessionBus.isConnected()) {
|
||||
qCWarning(KDECONNECT_PLUGIN_SENDNOTIFICATIONS) << "D-Bus connection failed";
|
||||
return;
|
||||
}
|
||||
|
||||
loadApplications();
|
||||
|
||||
connect(m_plugin->config(), &KdeConnectPluginConfig::configChanged, this, &NotificationsListener::loadApplications);
|
||||
|
||||
connect(m_thread, &NotificationsListenerThread::notificationReceived, this, &NotificationsListener::onNotify);
|
||||
const QString dbusServiceBus = QStringLiteral("org.freedesktop.DBus");
|
||||
const QString dbusPathDbus = QStringLiteral("/org/freedesktop/DBus");
|
||||
const QString dbusInterfaceMonitoring = QStringLiteral("org.freedesktop.DBus.Monitoring");
|
||||
const QString becomeMonitor = QStringLiteral("BecomeMonitor");
|
||||
const quint32 flags = 0;
|
||||
const QString match = QStringLiteral("interface='org.freedesktop.Notifications',member='Notify'");
|
||||
|
||||
m_thread->start();
|
||||
QObject::connect(sessionBus.interface(), SIGNAL(MessageReceived(QDBusMessage)), this, SLOT(handleMessage(QDBusMessage)));
|
||||
|
||||
QDBusMessage msg = QDBusMessage::createMethodCall(dbusServiceBus, dbusPathDbus, dbusInterfaceMonitoring, becomeMonitor);
|
||||
QStringList matches = {match};
|
||||
msg.setArguments(QList<QVariant>{QVariant(matches), QVariant(flags)});
|
||||
QDBusMessage reply = sessionBus.call(msg);
|
||||
if (reply.type() == QDBusMessage::ErrorMessage) {
|
||||
qCWarning(KDECONNECT_PLUGIN_SENDNOTIFICATIONS).noquote() << "Failed to become a DBus monitor."
|
||||
<< "No notifictions will be sent. Error:" << reply.errorMessage();
|
||||
}
|
||||
|
||||
NotificationsListener::~NotificationsListener()
|
||||
{
|
||||
m_thread->stop();
|
||||
m_thread->quit();
|
||||
}
|
||||
|
||||
void NotificationsListener::loadApplications()
|
||||
|
@ -354,7 +194,7 @@ QSharedPointer<QIODevice> NotificationsListener::iconForIconName(const QString &
|
|||
}
|
||||
}
|
||||
|
||||
void NotificationsListener::onNotify(const QString &appName,
|
||||
void NotificationsListener::handleNotification(const QString &appName,
|
||||
uint replacesId,
|
||||
const QString &appIcon,
|
||||
const QString &summary,
|
||||
|
|
|
@ -9,11 +9,10 @@
|
|||
|
||||
#include <QHash>
|
||||
#include <QIODevice>
|
||||
#include <QSet>
|
||||
#include <QSharedPointer>
|
||||
#include <QThread>
|
||||
#include <atomic>
|
||||
#include <dbus/dbus.h>
|
||||
#include <QDBusConnection>
|
||||
|
||||
#include "notifyingapplication.h"
|
||||
|
||||
class KdeConnectPlugin;
|
||||
|
||||
|
@ -21,21 +20,6 @@ struct NotifyingApplication;
|
|||
|
||||
#define PACKET_TYPE_NOTIFICATION QStringLiteral("kdeconnect.notification")
|
||||
|
||||
class NotificationsListenerThread : public QThread
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
void run() override;
|
||||
void stop();
|
||||
void handleNotifyCall(DBusMessage *message);
|
||||
|
||||
Q_SIGNALS:
|
||||
void notificationReceived(const QString &, uint, const QString &, const QString &, const QString &, const QStringList &, const QVariantMap &, int);
|
||||
|
||||
private:
|
||||
std::atomic<DBusConnection *> m_connection = nullptr;
|
||||
};
|
||||
|
||||
// TODO: make a singleton, shared for all devices
|
||||
class NotificationsListener : public QObject
|
||||
{
|
||||
|
@ -43,7 +27,6 @@ class NotificationsListener : public QObject
|
|||
|
||||
public:
|
||||
explicit NotificationsListener(KdeConnectPlugin *aPlugin);
|
||||
~NotificationsListener() override;
|
||||
|
||||
protected:
|
||||
KdeConnectPlugin *m_plugin;
|
||||
|
@ -63,11 +46,12 @@ protected:
|
|||
QSharedPointer<QIODevice> iconForIconName(const QString &iconName) const;
|
||||
QSharedPointer<QIODevice> iconFromQImage(const QImage &image) const;
|
||||
|
||||
void handleNotification(const QString &, uint, const QString &, const QString &, const QString &, const QStringList &, const QVariantMap &, int);
|
||||
|
||||
private Q_SLOTS:
|
||||
void handleMessage(const QDBusMessage& message);
|
||||
void loadApplications();
|
||||
void onNotify(const QString &, uint, const QString &, const QString &, const QString &, const QStringList &, const QVariantMap &, int);
|
||||
|
||||
private:
|
||||
QSharedPointer<QIODevice> pngFromImage();
|
||||
NotificationsListenerThread *m_thread;
|
||||
QDBusConnection sessionBus;
|
||||
};
|
||||
|
|
Loading…
Reference in a new issue