From 7ee859f6a5e6a334a172015fce744baeff539050 Mon Sep 17 00:00:00 2001 From: Javier Date: Sun, 26 Apr 2015 00:15:06 +0200 Subject: drop c++11 requirement, use private classes --- notificationmonitor.cpp | 371 ++++++++++++++++++++++++++---------------------- 1 file changed, 200 insertions(+), 171 deletions(-) (limited to 'notificationmonitor.cpp') diff --git a/notificationmonitor.cpp b/notificationmonitor.cpp index 339f43c..25948ee 100644 --- a/notificationmonitor.cpp +++ b/notificationmonitor.cpp @@ -1,71 +1,161 @@ -#include +#include #include #include #include "notification.h" #include "notificationmonitor.h" -using namespace watchfish; +namespace watchfish +{ + +Q_LOGGING_CATEGORY(notificationMonitorCat, "watchfish-NotificationMonitor") -namespace +class NotificationMonitorPrivate { + NotificationMonitor * const q_ptr; + Q_DECLARE_PUBLIC(NotificationMonitor) + + /** The current set of monitored notifications, indexed by id. */ + QMap _notifs; + /** Low level dbus connection used for sniffing. */ + DBusConnection *_conn; + /** Serials of DBUS method calls of which we are expecting a reply. */ + QHash _pending_confirmation; + + NotificationMonitorPrivate(NotificationMonitor *q); + ~NotificationMonitorPrivate(); + + void processIncomingNotification(quint32 id, const QVariantHash &content); + void processCloseNotification(quint32 id, quint32 reason); + + void sendMessageWithString(const char *service, const char *path, const char *iface, const char *method, const char *arg); + void addMatchRule(const char *rule); + void removeMatchRule(const char *rule); -NotificationMonitor *global_monitor = 0; + QVariantHash parseNotifyCall(DBusMessage *msg) const; -DBusConnection *bus_connection; + static dbus_bool_t busWatchAdd(DBusWatch *watch, void *data); + static void busWatchRemove(DBusWatch *watch, void *data); + static void busWatchToggle(DBusWatch *watch, void *data); -QHash pending_confirmation; + static DBusHandlerResult busMessageFilter(DBusConnection *conn, DBusMessage *msg, void *user_data); -dbus_bool_t bus_watch_add(DBusWatch *watch, void *data) + void handleBusSocketActivated(); +}; + +NotificationMonitorPrivate::NotificationMonitorPrivate(NotificationMonitor *q) + : q_ptr(q) { - NotificationMonitor *monitor = static_cast(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; + DBusError error = DBUS_ERROR_INIT; + _conn = dbus_bus_get_private(DBUS_BUS_SESSION, &error); + if (!_conn) { + qCWarning(notificationMonitorCat) << "Could not connect to the session bus"; + return; } - QSocketNotifier *notifier = new QSocketNotifier(socket, type, monitor); - dbus_watch_set_data(watch, notifier, NULL); + dbus_connection_set_exit_on_disconnect(_conn, FALSE); - notifier->setEnabled(dbus_watch_get_enabled(watch)); + dbus_connection_set_watch_functions(_conn, busWatchAdd, busWatchRemove, + busWatchToggle, this, NULL); - notifier->connect(notifier, &QSocketNotifier::activated, - [watch]() { - dbus_watch_handle(watch, dbus_watch_get_flags(watch)); + addMatchRule("type='method_call',interface='org.freedesktop.Notifications',member='Notify',eavesdrop='true'"); + addMatchRule("type='method_return',sender='org.freedesktop.Notifications',eavesdrop='true'"); + addMatchRule("type='signal',sender='org.freedesktop.Notifications',path='/org/freedesktop/Notifications',interface='org.freedesktop.Notifications',member='NotificationClosed'"); - while (dbus_connection_get_dispatch_status(bus_connection) == DBUS_DISPATCH_DATA_REMAINS) { - dbus_connection_dispatch(bus_connection); - } - }); + dbus_bool_t result = dbus_connection_add_filter(_conn, busMessageFilter, + this, NULL); + if (!result) { + qCWarning(notificationMonitorCat) << "Could not add filter"; + } - return TRUE; + qCDebug(notificationMonitorCat) << "Starting notification monitor"; } -void bus_watch_remove(DBusWatch *watch, void *data) +NotificationMonitorPrivate::~NotificationMonitorPrivate() { - QSocketNotifier *notifier = static_cast(dbus_watch_get_data(watch)); - Q_UNUSED(data); - delete notifier; + QMap::iterator it = _notifs.begin(); + while (it != _notifs.end()) { + delete it.value(); + } + + removeMatchRule("type='method_call',interface='org.freedesktop.Notifications',member='Notify',eavesdrop='true'"); + removeMatchRule("type='method_return',sender='org.freedesktop.Notifications',eavesdrop='true'"); + removeMatchRule("type='signal',sender='org.freedesktop.Notifications',path='/org/freedesktop/Notifications',interface='org.freedesktop.Notifications',member='NotificationClosed'"); + + dbus_connection_remove_filter(_conn, busMessageFilter, this); + + dbus_connection_close(_conn); + dbus_connection_unref(_conn); } -void bus_watch_toggle(DBusWatch *watch, void *data) +void NotificationMonitorPrivate::processIncomingNotification(quint32 id, const QVariantHash &content) { - QSocketNotifier *notifier = static_cast(dbus_watch_get_data(watch)); - Q_UNUSED(data); - notifier->setEnabled(dbus_watch_get_enabled(watch)); + Q_Q(NotificationMonitor); + qCDebug(notificationMonitorCat) << "Incoming notification" << id << content; + + Notification *n = _notifs.value(id, 0); + + bool is_new_notification = !n; + if (is_new_notification) { + n = new Notification(id, q); + } + + n->setSender(content["sender"].toString()); + n->setSummary(content["summary"].toString()); + n->setBody(content["body"].toString()); + n->setIcon(content["icon"].toString()); + + QDateTime timestamp = content["timestamp"].toDateTime(); + if (timestamp.isValid()) { + n->setTimestamp(timestamp); + } else if (is_new_notification) { + n->setTimestamp(QDateTime::currentDateTime()); + } + + if (is_new_notification) { + _notifs.insert(id, n); + emit q->notification(n); + } } -QVariantHash parse_notify_call(DBusMessage *msg) +void NotificationMonitorPrivate::processCloseNotification(quint32 id, quint32 reason) +{ + qCDebug(notificationMonitorCat) << "Close notification" << id << reason; + Notification *n = _notifs.value(id, 0); + if (n) { + _notifs.remove(id); + emit n->closed(static_cast(reason)); + n->deleteLater(); + } else { + qCDebug(notificationMonitorCat) << " but it is not found"; + } +} + +void NotificationMonitorPrivate::sendMessageWithString(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(_conn, msg, NULL); + dbus_message_unref(msg); +} + +void NotificationMonitorPrivate::addMatchRule(const char *rule) +{ + sendMessageWithString("org.freedesktop.DBus", "/", + "org.freedesktop.DBus", "AddMatch", rule); +} + +void NotificationMonitorPrivate::removeMatchRule(const char *rule) +{ + sendMessageWithString("org.freedesktop.DBus", "/", + "org.freedesktop.DBus", "RemoveMatch", rule); +} + +QVariantHash NotificationMonitorPrivate::parseNotifyCall(DBusMessage *msg) const { QVariantHash r; DBusMessageIter iter, sub; @@ -74,7 +164,7 @@ QVariantHash parse_notify_call(DBusMessage *msg) qint32 expire_timeout; if (strcmp(dbus_message_get_signature(msg), "susssasa{sv}i") != 0) { - qWarning() << "Invalid signature"; + qCWarning(notificationMonitorCat) << "Invalid signature"; return r; } @@ -143,27 +233,73 @@ QVariantHash parse_notify_call(DBusMessage *msg) return r; } -DBusHandlerResult message_filter(DBusConnection *conn, DBusMessage *msg, void *user_data) +dbus_bool_t NotificationMonitorPrivate::busWatchAdd(DBusWatch *watch, void *data) +{ + NotificationMonitorPrivate *self = static_cast(data); + NotificationMonitor *monitor = self->q_func(); + 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: + qCWarning(notificationMonitorCat) << "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->setProperty("dbus-watch", QVariant::fromValue(watch)); + + notifier->connect(notifier, SIGNAL(activated(int)), + monitor, SLOT(handleBusSocketActivated())); + + return TRUE; +} + +void NotificationMonitorPrivate::busWatchRemove(DBusWatch *watch, void *data) { - NotificationMonitor *monitor = static_cast(user_data); + QSocketNotifier *notifier = static_cast(dbus_watch_get_data(watch)); + Q_UNUSED(data); + delete notifier; +} + +void NotificationMonitorPrivate::busWatchToggle(DBusWatch *watch, void *data) +{ + QSocketNotifier *notifier = static_cast(dbus_watch_get_data(watch)); + Q_UNUSED(data); + notifier->setEnabled(dbus_watch_get_enabled(watch)); +} + +DBusHandlerResult NotificationMonitorPrivate::busMessageFilter(DBusConnection *conn, DBusMessage *msg, void *user_data) +{ + NotificationMonitorPrivate *self = static_cast(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); + QVariantHash content = self->parseNotifyCall(msg); + self->_pending_confirmation.insert(serial, content); } break; case DBUS_MESSAGE_TYPE_METHOD_RETURN: - if (pending_confirmation.contains(dbus_message_get_reply_serial(msg))) { + if (self->_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); + QVariantHash content = self->_pending_confirmation.take(dbus_message_get_reply_serial(msg)); + self->processIncomingNotification(id, content); } else { - qWarning() << "Could not parse notification method return"; + qCWarning(notificationMonitorCat) << "Could not parse notification method return"; } } break; @@ -174,149 +310,42 @@ DBusHandlerResult message_filter(DBusConnection *conn, DBusMessage *msg, void *u DBUS_TYPE_UINT32, &id, DBUS_TYPE_UINT32, &reason, DBUS_TYPE_INVALID)) { - monitor->processCloseNotification(id, reason); + self->processCloseNotification(id, reason); } else { - qWarning() << "Failed to parse notification signal arguments"; + qCWarning(notificationMonitorCat) << "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); + return DBUS_HANDLER_RESULT_HANDLED; } -void add_match_rule(const char *rule) +void NotificationMonitorPrivate::handleBusSocketActivated() { - send_message_with_string("org.freedesktop.DBus", "/", - "org.freedesktop.DBus", "AddMatch", rule); -} + Q_Q(NotificationMonitor); + QSocketNotifier *notifier = static_cast(q->sender()); + DBusWatch *watch = static_cast(notifier->property("dbus-watch").value()); -void remove_match_rule(const char *rule) -{ - send_message_with_string("org.freedesktop.DBus", "/", - "org.freedesktop.DBus", "RemoveMatch", rule); -} + dbus_watch_handle(watch, dbus_watch_get_flags(watch)); + while (dbus_connection_get_dispatch_status(_conn) == DBUS_DISPATCH_DATA_REMAINS) { + dbus_connection_dispatch(_conn); + } } NotificationMonitor::NotificationMonitor(QObject *parent) : - QObject(parent) + QObject(parent), d_ptr(new NotificationMonitorPrivate(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() { - 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; + delete d_ptr; } -NotificationMonitor *NotificationMonitor::instance() -{ - if (!global_monitor) { - global_monitor = new NotificationMonitor; - } - return global_monitor; } -void NotificationMonitor::processIncomingNotification(quint32 id, const QVariantHash &content) -{ - qDebug() << "Incoming notification" << id << content; - Notification *n = _notifs.value(id, 0); - if (n) { - QString s = content["summary"].toString(); - if (n->_summary != s) { - n->_summary = s; - emit n->summaryChanged(); - } - s = content["body"].toString(); - if (n->_body != s) { - n->_body = s; - emit n->bodyChanged(); - } - s = content["icon"].toString(); - if (n->_icon != s) { - n->_icon = s; - emit n->iconChanged(); - } - QDateTime dt = content["timestamp"].toDateTime(); - if (dt.isValid() && n->_timestamp != dt) { - n->_timestamp = dt; - emit n->timestampChanged(); - } - } else { - n = new Notification(this); - n->_id = id; - n->_sender = content["sender"].toString(); - n->_summary = content["summary"].toString(); - n->_body = content["body"].toString(); - n->_timestamp = content["timestamp"].toDateTime(); - n->_icon = content["icon"].toString(); - - if (!n->_timestamp.isValid()) { - n->_timestamp = QDateTime::currentDateTime(); - } - - _notifs.insert(id, n); - - emit notification(n); - } -} - -void NotificationMonitor::processCloseNotification(quint32 id, quint32 reason) -{ - qDebug() << "Close notification" << id << reason; - Notification *n = _notifs.value(id, 0); - if (n) { - _notifs.remove(id); - emit n->closed(reason); - n->deleteLater(); - } else { - qDebug() << " but it is not found"; - } -} +#include "moc_notificationmonitor.cpp" -- cgit v1.2.3