Replace libdbus with qdbus

This commit is contained in:
Albert Vaca Cintora 2023-08-11 11:32:58 +02:00
parent 590c81cf84
commit 2170cfe78e
2 changed files with 57 additions and 233 deletions

View file

@ -12,6 +12,9 @@
#include <QFile> #include <QFile>
#include <QIODevice> #include <QIODevice>
#include <QImage> #include <QImage>
#include <QDBusMessage>
#include <QDBusConnection>
#include <QDBusConnectionInterface>
#include <core/kdeconnectplugin.h> #include <core/kdeconnectplugin.h>
#include <core/kdeconnectpluginconfig.h> #include <core/kdeconnectpluginconfig.h>
@ -23,236 +26,73 @@
#include "plugin_sendnotifications_debug.h" #include "plugin_sendnotifications_debug.h"
#include "sendnotificationsplugin.h" #include "sendnotificationsplugin.h"
const char *NOTIFY_SIGNATURE = "susssasa{sv}i"; void NotificationsListener::handleMessage(const QDBusMessage& message)
QString becomeMonitor(DBusConnection *conn, const char *match)
{ {
// message qWarning() << message;
DBusMessage *msg = dbus_message_new_method_call(DBUS_SERVICE_DBUS, DBUS_PATH_DBUS, DBUS_INTERFACE_MONITORING, "BecomeMonitor");
Q_ASSERT(msg != nullptr);
// arguments if (message.interface() != QStringLiteral("org.freedesktop.Notifications") || message.member() != QStringLiteral("Notify")) {
const char *matches[] = {match}; qWarning() << "Wrong method";
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);
return; return;
} }
Q_ASSERT(m_connection != nullptr); const QString notifySignature = QStringLiteral("susssasa{sv}i");
dbus_connection_set_route_peer_messages(m_connection, true); if (message.signature() != notifySignature) {
dbus_connection_set_exit_on_disconnect(m_connection, false); qWarning() << "Wrong signature";
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;
} }
// wake up every minute to see if we are still connected QList<QVariant> args = message.arguments();
while (m_connection != nullptr) { if (args.isEmpty()) {
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);
return; return;
} }
QString appName = nextString(&iter); QString appName = args.at(0).toString();
uint replacesId = nextUnsigned(&iter); uint replacesId = args.at(1).toUInt();
QString appIcon = nextString(&iter); QString appIcon = args.at(2).toString();
QString summary = nextString(&iter); QString summary = args.at(3).toString();
QString body = nextString(&iter); QString body = args.at(4).toString();
QStringList actions = nextStringList(&iter); QStringList actions = args.at(5).toStringList();
QVariantMap hints = nextVariantMap(&iter); QVariantMap hints = args.at(6).toMap();
int timeout = nextInt(&iter); 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) NotificationsListener::NotificationsListener(KdeConnectPlugin *aPlugin)
: QObject(aPlugin) : QObject(aPlugin)
, m_plugin(aPlugin) , m_plugin(aPlugin)
, m_thread(new NotificationsListenerThread()) , sessionBus(QDBusConnection::sessionBus())
{ {
#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
qRegisterMetaTypeStreamOperators<NotifyingApplication>("NotifyingApplication"); qRegisterMetaTypeStreamOperators<NotifyingApplication>("NotifyingApplication");
#endif #endif
if (!sessionBus.isConnected()) {
qCWarning(KDECONNECT_PLUGIN_SENDNOTIFICATIONS) << "D-Bus connection failed";
return;
}
loadApplications(); loadApplications();
connect(m_plugin->config(), &KdeConnectPluginConfig::configChanged, this, &NotificationsListener::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)));
}
NotificationsListener::~NotificationsListener() QDBusMessage msg = QDBusMessage::createMethodCall(dbusServiceBus, dbusPathDbus, dbusInterfaceMonitoring, becomeMonitor);
{ QStringList matches = {match};
m_thread->stop(); msg.setArguments(QList<QVariant>{QVariant(matches), QVariant(flags)});
m_thread->quit(); 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();
}
} }
void NotificationsListener::loadApplications() void NotificationsListener::loadApplications()
@ -354,14 +194,14 @@ QSharedPointer<QIODevice> NotificationsListener::iconForIconName(const QString &
} }
} }
void NotificationsListener::onNotify(const QString &appName, void NotificationsListener::handleNotification(const QString &appName,
uint replacesId, uint replacesId,
const QString &appIcon, const QString &appIcon,
const QString &summary, const QString &summary,
const QString &body, const QString &body,
const QStringList &actions, const QStringList &actions,
const QVariantMap &hints, const QVariantMap &hints,
int timeout) int timeout)
{ {
Q_UNUSED(actions); Q_UNUSED(actions);

View file

@ -9,11 +9,10 @@
#include <QHash> #include <QHash>
#include <QIODevice> #include <QIODevice>
#include <QSet>
#include <QSharedPointer> #include <QSharedPointer>
#include <QThread> #include <QDBusConnection>
#include <atomic>
#include <dbus/dbus.h> #include "notifyingapplication.h"
class KdeConnectPlugin; class KdeConnectPlugin;
@ -21,21 +20,6 @@ struct NotifyingApplication;
#define PACKET_TYPE_NOTIFICATION QStringLiteral("kdeconnect.notification") #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 // TODO: make a singleton, shared for all devices
class NotificationsListener : public QObject class NotificationsListener : public QObject
{ {
@ -43,7 +27,6 @@ class NotificationsListener : public QObject
public: public:
explicit NotificationsListener(KdeConnectPlugin *aPlugin); explicit NotificationsListener(KdeConnectPlugin *aPlugin);
~NotificationsListener() override;
protected: protected:
KdeConnectPlugin *m_plugin; KdeConnectPlugin *m_plugin;
@ -63,11 +46,12 @@ protected:
QSharedPointer<QIODevice> iconForIconName(const QString &iconName) const; QSharedPointer<QIODevice> iconForIconName(const QString &iconName) const;
QSharedPointer<QIODevice> iconFromQImage(const QImage &image) 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: private Q_SLOTS:
void handleMessage(const QDBusMessage& message);
void loadApplications(); void loadApplications();
void onNotify(const QString &, uint, const QString &, const QString &, const QString &, const QStringList &, const QVariantMap &, int);
private: private:
QSharedPointer<QIODevice> pngFromImage(); QDBusConnection sessionBus;
NotificationsListenerThread *m_thread;
}; };