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 --- libwatchfish.pro | 2 +- notification.cpp | 104 +++++++++++++- notification.h | 64 ++++++--- notificationmonitor.cpp | 371 ++++++++++++++++++++++++++---------------------- notificationmonitor.h | 20 +-- 5 files changed, 356 insertions(+), 205 deletions(-) diff --git a/libwatchfish.pro b/libwatchfish.pro index 7967685..63026d5 100644 --- a/libwatchfish.pro +++ b/libwatchfish.pro @@ -3,7 +3,7 @@ TARGET = watchfish TEMPLATE = lib CONFIG += staticlib -CONFIG += c++11 link_pkgconfig +CONFIG += link_pkgconfig PKGCONFIG += dbus-1 INCLUDEPATH += /usr/include/dbus-1.0 diff --git a/notification.cpp b/notification.cpp index 09f61fb..24617b9 100644 --- a/notification.cpp +++ b/notification.cpp @@ -1,7 +1,107 @@ #include "notification.h" -using namespace watchfish; +namespace watchfish +{ + +struct NotificationPrivate +{ + uint id; + QString sender; + QString summary; + QString body; + QDateTime timestamp; + QString icon; +}; + +Notification::Notification(uint id, QObject *parent) : QObject(parent), d_ptr(new NotificationPrivate) +{ + Q_D(Notification); + d->id = id; +} + +Notification::~Notification() +{ +} + +uint Notification::id() const +{ + Q_D(const Notification); + return d->id; +} + +QString Notification::sender() const +{ + Q_D(const Notification); + return d->sender; +} + +void Notification::setSender(const QString &sender) +{ + Q_D(Notification); + if (sender != d->sender) { + d->sender = sender; + emit senderChanged(); + } +} + +QString Notification::summary() const +{ + Q_D(const Notification); + return d->summary; +} -Notification::Notification(QObject *parent) : QObject(parent) +void Notification::setSummary(const QString &summary) { + Q_D(Notification); + if (summary != d->summary) { + d->summary = summary; + emit summaryChanged(); + } +} + +QString Notification::body() const +{ + Q_D(const Notification); + return d->body; +} + +void Notification::setBody(const QString &body) +{ + Q_D(Notification); + if (body != d->body) { + d->body = body; + emit bodyChanged(); + } +} + +QDateTime Notification::timestamp() const +{ + Q_D(const Notification); + return d->timestamp; +} + +void Notification::setTimestamp(const QDateTime &dt) +{ + Q_D(Notification); + if (dt != d->timestamp) { + d->timestamp = dt; + emit timestampChanged(); + } +} + +QString Notification::icon() const +{ + Q_D(const Notification); + return d->icon; +} + +void Notification::setIcon(const QString &icon) +{ + Q_D(Notification); + if (icon != d->icon) { + d->icon = icon; + emit iconChanged(); + } +} + } diff --git a/notification.h b/notification.h index 46b6754..c6a9966 100644 --- a/notification.h +++ b/notification.h @@ -7,43 +7,63 @@ namespace watchfish { +class NotificationPrivate; + class Notification : public QObject { Q_OBJECT - Q_PROPERTY(uint id READ id CONSTANT) - Q_PROPERTY(QString sender READ sender) - Q_PROPERTY(QString summary READ summary NOTIFY summaryChanged) - Q_PROPERTY(QString body READ body NOTIFY bodyChanged) - Q_PROPERTY(QDateTime timestamp READ timestamp NOTIFY timestampChanged) - Q_PROPERTY(QString icon READ icon NOTIFY iconChanged) + Q_DECLARE_PRIVATE(Notification) - explicit Notification(QObject *parent = 0); + /** Notification ID */ + Q_PROPERTY(uint id READ id CONSTANT) + /** Name of sender program */ + Q_PROPERTY(QString sender READ sender WRITE setSender NOTIFY senderChanged) + Q_PROPERTY(QString summary READ summary WRITE setSummary NOTIFY summaryChanged) + Q_PROPERTY(QString body READ body WRITE setBody NOTIFY bodyChanged) + Q_PROPERTY(QDateTime timestamp READ timestamp WRITE setTimestamp NOTIFY timestampChanged) + /** Icon file path */ + Q_PROPERTY(QString icon READ icon WRITE setIcon NOTIFY iconChanged) + Q_ENUMS(CloseReason) public: - inline uint id() const { return _id; } - inline QString sender() const { return _sender; } - inline QString summary() const { return _summary; } - inline QString body() const { return _body; } - inline QDateTime timestamp() const { return _timestamp; } - inline QString icon() const { return _icon; } + explicit Notification(uint id, QObject *parent = 0); + ~Notification(); + + enum CloseReason { + Expired = 1, + DismissedByUser = 2, + DismissedByProgram = 3, + ClosedOther = 4 + }; + + uint id() const; + + QString sender() const; + void setSender(const QString &sender); + + QString summary() const; + void setSummary(const QString &summary); + + QString body() const; + void setBody(const QString &body); + + QDateTime timestamp() const; + void setTimestamp(const QDateTime &dt); + + QString icon() const; + void setIcon(const QString &icon); signals: + void senderChanged(); void summaryChanged(); void bodyChanged(); void timestampChanged(); void iconChanged(); - void closed(int reason); + void closed(CloseReason reason); private: - friend class NotificationMonitor; - - uint _id; - QString _sender; - QString _summary; - QString _body; - QDateTime _timestamp; - QString _icon; + NotificationPrivate * const d_ptr; }; } 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" diff --git a/notificationmonitor.h b/notificationmonitor.h index 8563053..1cb7cca 100644 --- a/notificationmonitor.h +++ b/notificationmonitor.h @@ -1,32 +1,34 @@ #ifndef NOTIFICATIONMONITOR_H #define NOTIFICATIONMONITOR_H -#include +#include #include +#include + +#include "notification.h" namespace watchfish { -class Notification; +Q_DECLARE_LOGGING_CATEGORY(notificationMonitorCat) + +class NotificationMonitorPrivate; class NotificationMonitor : public QObject { Q_OBJECT + Q_DECLARE_PRIVATE(NotificationMonitor) public: + explicit NotificationMonitor(QObject *parent = 0); ~NotificationMonitor(); - static NotificationMonitor *instance(); - - void processIncomingNotification(quint32 id, const QVariantHash &content); - void processCloseNotification(quint32 id, quint32 reason); - signals: void notification(Notification *n); private: - explicit NotificationMonitor(QObject *parent = 0); - QMap _notifs; + Q_PRIVATE_SLOT(d_func(), void handleBusSocketActivated()) + NotificationMonitorPrivate * const d_ptr; }; } -- cgit v1.2.3