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 <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);
|
||||||
|
|
||||||
|
|
|
@ -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;
|
|
||||||
};
|
};
|
||||||
|
|
Loading…
Reference in a new issue