diff options
author | Javier <dev.git@javispedro.com> | 2015-03-30 01:05:47 +0200 |
---|---|---|
committer | Javier <dev.git@javispedro.com> | 2015-03-30 01:05:47 +0200 |
commit | efe35bf6c6ca8cce73e97f303963a51862778600 (patch) | |
tree | 3e6a47926473501ba953cd716df219af0dd478fb /saltoqd/notificationmonitor.cpp | |
parent | 2ded0dbcf63ae7203f64a8f788cc74528eb79416 (diff) | |
download | saltoq-efe35bf6c6ca8cce73e97f303963a51862778600.tar.gz saltoq-efe35bf6c6ca8cce73e97f303963a51862778600.zip |
experiment with new notificationmonitor approach
Diffstat (limited to 'saltoqd/notificationmonitor.cpp')
-rw-r--r-- | saltoqd/notificationmonitor.cpp | 287 |
1 files changed, 273 insertions, 14 deletions
diff --git a/saltoqd/notificationmonitor.cpp b/saltoqd/notificationmonitor.cpp index f22ce02..2c6621d 100644 --- a/saltoqd/notificationmonitor.cpp +++ b/saltoqd/notificationmonitor.cpp @@ -1,30 +1,254 @@ #include <QtCore/QDebug> -#include <QtGui/QIcon> -#include <QtDBus/QDBusConnection> -#include <QtDBus/QDBusConnectionInterface> +#include <QtCore/QSocketNotifier> +#include <dbus/dbus.h> #include "notificationmonitor.h" #include "notifications_adaptor.h" -static NotificationMonitor *global_monitor = 0; +namespace +{ + +NotificationMonitor *global_monitor = 0; + +DBusConnection *bus_connection; + +QHash<quint32, QVariantHash> pending_confirmation; + +dbus_bool_t bus_watch_add(DBusWatch *watch, void *data) +{ + NotificationMonitor *monitor = static_cast<NotificationMonitor*>(data); + int socket = dbus_watch_get_socket(watch); + int flags = dbus_watch_get_flags(watch); + QSocketNotifier::Type type; + switch (flags) { + case DBUS_WATCH_READABLE: + type = QSocketNotifier::Read; + break; + case DBUS_WATCH_WRITABLE: + type = QSocketNotifier::Write; + break; + default: + qWarning() << "Can't add this type of watch" << flags; + return FALSE; + } + + QSocketNotifier *notifier = new QSocketNotifier(socket, type, monitor); + dbus_watch_set_data(watch, notifier, NULL); + + notifier->setEnabled(dbus_watch_get_enabled(watch)); + + notifier->connect(notifier, &QSocketNotifier::activated, + [watch]() { + dbus_watch_handle(watch, dbus_watch_get_flags(watch)); + + while (dbus_connection_get_dispatch_status(bus_connection) == DBUS_DISPATCH_DATA_REMAINS) { + dbus_connection_dispatch(bus_connection); + } + }); + + return TRUE; +} + +void bus_watch_remove(DBusWatch *watch, void *data) +{ + QSocketNotifier *notifier = static_cast<QSocketNotifier*>(dbus_watch_get_data(watch)); + Q_UNUSED(data); + delete notifier; +} + +void bus_watch_toggle(DBusWatch *watch, void *data) +{ + QSocketNotifier *notifier = static_cast<QSocketNotifier*>(dbus_watch_get_data(watch)); + Q_UNUSED(data); + notifier->setEnabled(dbus_watch_get_enabled(watch)); +} + +QVariantHash parse_notify_call(DBusMessage *msg) +{ + QVariantHash r; + DBusMessageIter iter, sub; + const char *app_name, *app_icon, *summary, *body; + quint32 replaces_id; + qint32 expire_timeout; + + if (strcmp(dbus_message_get_signature(msg), "susssasa{sv}i") != 0) { + qWarning() << "Invalid signature"; + return r; + } + + dbus_message_iter_init(msg, &iter); + Q_ASSERT(dbus_message_iter_get_arg_type(&iter) == DBUS_TYPE_STRING); + dbus_message_iter_get_basic(&iter, &app_name); + dbus_message_iter_next(&iter); + Q_ASSERT(dbus_message_iter_get_arg_type(&iter) == DBUS_TYPE_UINT32); + dbus_message_iter_get_basic(&iter, &replaces_id); + dbus_message_iter_next(&iter); + Q_ASSERT(dbus_message_iter_get_arg_type(&iter) == DBUS_TYPE_STRING); + dbus_message_iter_get_basic(&iter, &app_icon); + dbus_message_iter_next(&iter); + dbus_message_iter_get_basic(&iter, &summary); + dbus_message_iter_next(&iter); + dbus_message_iter_get_basic(&iter, &body); + dbus_message_iter_next(&iter); + + QStringList actions; + dbus_message_iter_recurse(&iter, &sub); + while (dbus_message_iter_get_arg_type(&sub) == DBUS_TYPE_STRING) { + const char *action; + dbus_message_iter_get_basic(&sub, &action); + actions.append(QString::fromUtf8(action)); + dbus_message_iter_next(&sub); + } + r.insert("actions", QVariant::fromValue(actions)); + dbus_message_iter_next(&iter); + + dbus_message_iter_recurse(&iter, &sub); + while (dbus_message_iter_get_arg_type(&sub) == DBUS_TYPE_DICT_ENTRY) { + DBusMessageIter entry, value; + const char *key; + + dbus_message_iter_recurse(&sub, &entry); + dbus_message_iter_get_basic(&entry, &key); + dbus_message_iter_next(&entry); + + dbus_message_iter_recurse(&entry, &value); + if (strcmp(key, "category") == 0 && dbus_message_iter_get_arg_type(&value) == DBUS_TYPE_STRING) { + const char *s; + dbus_message_iter_get_basic(&value, &s); + r.insert("category", QString::fromUtf8(s)); + } + + dbus_message_iter_next(&sub); + } + + dbus_message_iter_next(&iter); + Q_ASSERT(dbus_message_iter_get_arg_type(&iter) == DBUS_TYPE_INT32); + dbus_message_iter_get_basic(&iter, &expire_timeout); + + r.insert("sender", QString::fromUtf8(app_name)); + r.insert("app_icon", QString::fromUtf8(app_icon)); + r.insert("summary", QString::fromUtf8(summary)); + r.insert("body", QString::fromUtf8(body)); + + return r; +} + +DBusHandlerResult message_filter(DBusConnection *conn, DBusMessage *msg, void *user_data) +{ + NotificationMonitor *monitor = static_cast<NotificationMonitor*>(user_data); + DBusError error = DBUS_ERROR_INIT; + Q_UNUSED(conn); + switch (dbus_message_get_type(msg)) { + case DBUS_MESSAGE_TYPE_METHOD_CALL: + if (dbus_message_is_method_call(msg, "org.freedesktop.Notifications", "Notify")) { + quint32 serial = dbus_message_get_serial(msg); + QVariantHash content = parse_notify_call(msg); + pending_confirmation.insert(serial, content); + } + break; + case DBUS_MESSAGE_TYPE_METHOD_RETURN: + if (pending_confirmation.contains(dbus_message_get_reply_serial(msg))) { + quint32 id; + if (dbus_message_get_args(msg, &error, DBUS_TYPE_UINT32, &id, DBUS_TYPE_INVALID)) { + QVariantHash content = pending_confirmation.take(dbus_message_get_reply_serial(msg)); + monitor->processIncomingNotification(id, content); + } else { + qWarning() << "Could not parse notification method return"; + } + } + break; + case DBUS_MESSAGE_TYPE_SIGNAL: + if (dbus_message_is_signal(msg, "org.freedesktop.Notifications", "NotificationClosed")) { + quint32 id, reason; + if (dbus_message_get_args(msg, &error, + DBUS_TYPE_UINT32, &id, + DBUS_TYPE_UINT32, &reason, + DBUS_TYPE_INVALID)) { + monitor->processCloseNotification(id, reason); + } else { + qWarning() << "Failed to parse notification signal arguments"; + } + + } + break; + } + return DBUS_HANDLER_RESULT_HANDLED; +} + +void send_message_with_string(const char *service, const char *path, const char *iface, const char *method, const char *arg) +{ + DBusMessage *msg = dbus_message_new_method_call(service, path, iface, method); + Q_ASSERT(msg); + dbus_message_set_no_reply(msg, TRUE); + dbus_message_append_args(msg, + DBUS_TYPE_STRING, &arg, + DBUS_TYPE_INVALID); + dbus_connection_send(bus_connection, msg, NULL); + dbus_message_unref(msg); +} + +void add_match_rule(const char *rule) +{ + send_message_with_string("org.freedesktop.DBus", "/", + "org.freedesktop.DBus", "AddMatch", rule); +} + +void remove_match_rule(const char *rule) +{ + send_message_with_string("org.freedesktop.DBus", "/", + "org.freedesktop.DBus", "RemoveMatch", rule); +} + +} + +IncomingNotification::IncomingNotification(QObject *parent) : + QObject(parent) +{ +} NotificationMonitor::NotificationMonitor(QObject *parent) : QObject(parent) { - QDBusConnection bus = QDBusConnection::sessionBus(); - QDBusConnectionInterface *dbus = bus.interface(); - dbus->call("AddMatch", - "interface='org.freedesktop.Notifications',member='Notify',type='method_call',eavesdrop='true'"); - new NotificationsAdaptor(this); - bus.registerObject("/org/freedesktop/Notifications", this); + Q_ASSERT(!bus_connection); + DBusError error = DBUS_ERROR_INIT; + bus_connection = dbus_bus_get_private(DBUS_BUS_SESSION, &error); + if (!bus_connection) { + qWarning() << "Could not connect to the session bus"; + return; + } + + dbus_connection_set_exit_on_disconnect(bus_connection, FALSE); + + dbus_connection_set_watch_functions(bus_connection, bus_watch_add, + bus_watch_remove, bus_watch_toggle, + this, NULL); + + add_match_rule("type='method_call',interface='org.freedesktop.Notifications',member='Notify',eavesdrop='true'"); + add_match_rule("type='method_return',sender='org.freedesktop.Notifications',eavesdrop='true'"); + add_match_rule("type='signal',sender='org.freedesktop.Notifications',path='/org/freedesktop/Notifications',interface='org.freedesktop.Notifications',member='NotificationClosed'"); + + dbus_bool_t result = dbus_connection_add_filter(bus_connection, message_filter, + this, NULL); + if (!result) { + qWarning() << "Could not add filter"; + } + + qDebug() << "Starting notification monitor"; } NotificationMonitor::~NotificationMonitor() { - QDBusConnection bus = QDBusConnection::sessionBus(); - QDBusConnectionInterface *dbus = bus.interface(); - dbus->call("RemoveMatch", - "interface='org.freedesktop.Notifications',member='Notify',type='method_call',eavesdrop='true'"); + Q_ASSERT(bus_connection); + + remove_match_rule("type='method_call',interface='org.freedesktop.Notifications',member='Notify',eavesdrop='true'"); + remove_match_rule("type='method_return',sender='org.freedesktop.Notifications',eavesdrop='true'"); + remove_match_rule("type='signal',sender='org.freedesktop.Notifications',path='/org/freedesktop/Notifications',interface='org.freedesktop.Notifications',member='NotificationClosed'"); + + dbus_connection_remove_filter(bus_connection, message_filter, this); + + dbus_connection_close(bus_connection); + dbus_connection_unref(bus_connection); + bus_connection = NULL; } NotificationMonitor *NotificationMonitor::instance() @@ -35,6 +259,33 @@ NotificationMonitor *NotificationMonitor::instance() return global_monitor; } +void NotificationMonitor::processIncomingNotification(quint32 id, const QVariantHash &content) +{ + qDebug() << "Incoming notification" << id << content; + IncomingNotification *n = _notifs.value(id, 0); + if (n) { + // TODO emit changed signals for individual fields + } else { + n = new IncomingNotification(this); + n->_sender = content["app_name"].toString(); + n->_summary = content["summary"].toString(); + + emit notification(n); + } +} + +void NotificationMonitor::processCloseNotification(quint32 id, quint32 reason) +{ + qDebug() << "Close notification" << id << reason; + IncomingNotification *n = _notifs.value(id, 0); + if (n) { + _notifs.remove(id); + n->deleteLater(); + } +} + +#if 0 + uint NotificationMonitor::Notify(const QString &app_name, uint replaces_id, const QString &app_icon, const QString &summary, const QString &body, const QStringList &actions, const QVariantHash &hints, int expire_timeout) { QIcon icon; @@ -77,3 +328,11 @@ uint NotificationMonitor::Notify(const QString &app_name, uint replaces_id, cons return 0; } + +void NotificationMonitor::CloseNotification(uint id) +{ + qDebug() << "Close notification" << id; +} + +#endif + |