[pulseaudio-discuss] [PATCH 7/8] desktop-notifications: Added a filter to listen for ActionInvoked and NotificationClosed signals and improved how reply handlers are called.
Ștefan Săftescu
stefan.saftescu at gmail.com
Thu Jul 12 09:10:28 PDT 2012
---
.../module-ui-notification-dn-backend.c | 172 +++++++++++++++++---
1 file changed, 145 insertions(+), 27 deletions(-)
diff --git a/src/modules/notifications/module-ui-notification-dn-backend.c b/src/modules/notifications/module-ui-notification-dn-backend.c
index aa13687..51fb0b7 100644
--- a/src/modules/notifications/module-ui-notification-dn-backend.c
+++ b/src/modules/notifications/module-ui-notification-dn-backend.c
@@ -53,7 +53,8 @@ struct backend_userdata {
pa_hashmap* displaying;
pa_idxset* cancelling;
PA_LLIST_HEAD(pa_dbus_pending, pending_send);
- PA_LLIST_HEAD(pa_dbus_pending, pending_cancel);
+
+ bool filter_set;
};
struct module_userdata {
@@ -79,12 +80,14 @@ static pa_dbus_pending* pa_dbus_send_message(
/* TODO: pending != NULL */
p = pa_dbus_pending_new(conn, msg, pending, context_data, call_data);
- dbus_pending_call_set_notify(pending, func, p, NULL);
+
+ if(func)
+ dbus_pending_call_set_notify(pending, func, p, NULL);
return p;
}
-static void cancel_notification_reply(DBusPendingCall *pending, void *userdata) {
+/*static void cancel_notification_reply(DBusPendingCall *pending, void *userdata) {
DBusError err;
DBusMessage *msg;
pa_ui_notification_backend *backend;
@@ -105,23 +108,21 @@ static void cancel_notification_reply(DBusPendingCall *pending, void *userdata)
if (dbus_message_is_error(msg, DBUS_ERROR_SERVICE_UNKNOWN)) {
pa_log_debug("No Notifications server registered.");
- /* TODO: notification reply error */
-
goto finish;
}
if (dbus_message_get_type(msg) == DBUS_MESSAGE_TYPE_ERROR) {
pa_log_error("org.freedesktop.Notifications.CancelNotification() failed: %s: %s", dbus_message_get_error_name(msg), pa_dbus_get_error_message(msg));
+
goto finish;
}
- /* TODO: notificatioin reply cancel */
finish:
dbus_message_unref(msg);
PA_LLIST_REMOVE(pa_dbus_pending, u->pending_cancel, p);
pa_dbus_pending_free(p);
-}
+} */
static inline void cancel_notification_dbus(pa_ui_notification_backend *backend, pa_ui_notification *notification, unsigned *dbus_notification_id) {
DBusConnection *conn;
@@ -135,9 +136,10 @@ static inline void cancel_notification_dbus(pa_ui_notification_backend *backend,
msg = dbus_message_new_method_call("org.freedesktop.Notifications", "/org/freedesktop/Notifications", "org.freedesktop.Notifications", "CloseNotification");
pa_assert_se(dbus_message_append_args(msg, DBUS_TYPE_UINT32, dbus_notification_id, DBUS_TYPE_INVALID));
- p = pa_dbus_send_message(conn, msg, cancel_notification_reply, backend, notification);
+ dbus_message_set_no_reply(msg, TRUE);
+ p = pa_dbus_send_message(conn, msg, NULL, NULL, NULL);
- PA_LLIST_PREPEND(pa_dbus_pending, u->pending_cancel, p);
+ pa_dbus_pending_free(p);
pa_xfree(dbus_notification_id);
}
@@ -166,23 +168,31 @@ static void send_notification_reply(DBusPendingCall *pending, void *userdata) {
if (dbus_message_is_error(msg, DBUS_ERROR_SERVICE_UNKNOWN)) {
pa_log_debug("No Notifications server registered.");
- /* TODO: notification reply error */
+ notification->handle_reply(pa_ui_notification_reply_new(PA_UI_NOTIFCATION_REPLY_ERROR, notification, NULL));
goto finish;
}
if (dbus_message_get_type(msg) == DBUS_MESSAGE_TYPE_ERROR) {
pa_log_error("org.freedesktop.Notifications.Notify() failed: %s: %s", dbus_message_get_error_name(msg), pa_dbus_get_error_message(msg));
+
+ notification->handle_reply(pa_ui_notification_reply_new(PA_UI_NOTIFCATION_REPLY_ERROR, notification, NULL));
+
goto finish;
}
if(!dbus_message_get_args(msg, &err, DBUS_TYPE_UINT32, dbus_notification_id, DBUS_TYPE_INVALID)) {
pa_log_error("Failed to parse org.freedesktop.Notifications.Notify(): %s", err.message);
+
+ notification->handle_reply(pa_ui_notification_reply_new(PA_UI_NOTIFCATION_REPLY_ERROR, notification, NULL));
+
goto finish;
}
if (pa_idxset_remove_by_data(u->cancelling, notification, NULL)) {
cancel_notification_dbus(backend, notification, dbus_notification_id);
+
+ notification->handle_reply(pa_ui_notification_reply_new(PA_UI_NOTIFCATION_REPLY_ERROR, notification, NULL));
} else {
pa_hashmap_put(u->displaying, notification, dbus_notification_id);
}
@@ -197,8 +207,10 @@ finish:
static void send_notification(pa_ui_notification_backend *b, pa_ui_notification *n) {
DBusConnection *conn;
DBusMessage *msg;
- DBusMessageIter args, dict_iter;
+ DBusMessageIter args, dict_iter, array_iter;
pa_dbus_pending *p;
+ char *key, *value;
+ void *state;
struct backend_userdata *u;
u = b->userdata;
@@ -215,7 +227,14 @@ static void send_notification(pa_ui_notification_backend *b, pa_ui_notification
pa_assert_se(dbus_message_iter_append_basic(&args, DBUS_TYPE_STRING, (void *) &n->summary));
pa_assert_se(dbus_message_iter_append_basic(&args, DBUS_TYPE_STRING, (void *) &n->body));
- pa_dbus_append_basic_array(&args, DBUS_TYPE_STRING, (void *) n->actions, n->num_actions);
+ pa_assert_se(dbus_message_iter_open_container(&args, DBUS_TYPE_ARRAY, "s", &array_iter));
+
+ PA_HASHMAP_FOREACH_KEY(value, key, n->actions, state) {
+ pa_assert_se(dbus_message_iter_append_basic(&array_iter, DBUS_TYPE_STRING, (void *) &key));
+ pa_assert_se(dbus_message_iter_append_basic(&array_iter, DBUS_TYPE_STRING, (void *) &value));
+ }
+
+ pa_assert_se(dbus_message_iter_close_container(&args, &array_iter));
pa_assert_se(dbus_message_iter_open_container(&args, DBUS_TYPE_ARRAY, "{sv}", &dict_iter));
@@ -238,11 +257,92 @@ static void cancel_notification(pa_ui_notification_backend *backend, pa_ui_notif
if ((dbus_notification_id = pa_hashmap_remove(u->displaying, notification))) {
cancel_notification_dbus(backend, notification, dbus_notification_id);
+
+ notification->handle_reply(pa_ui_notification_reply_new(PA_UI_NOTIFCATION_REPLY_CANCELLED, notification, NULL));
} else {
pa_idxset_put(u->cancelling, notification, NULL);
}
}
+static DBusHandlerResult signal_cb(DBusConnection *conn, DBusMessage *msg, void *userdata) {
+ DBusError err;
+ pa_ui_notification_backend *backend;
+ pa_ui_notification *notification;
+ struct backend_userdata *u;
+ void *state;
+
+ unsigned dbus_notification_id, reason, *id;
+ char *action_key;
+
+ pa_assert(conn);
+ pa_assert(msg);
+ pa_assert_se(backend = userdata);
+
+ /* pa_log_debug("Message received: %s.%s", dbus_message_get_interface(msg), dbus_message_get_member(msg)); */
+
+ u = backend->userdata;
+
+ dbus_error_init(&err);
+ if(dbus_message_is_signal(msg, "org.freedesktop.Notifications", "NotificationClosed")) {
+ if (!dbus_message_get_args(msg, &err, DBUS_TYPE_UINT32, &dbus_notification_id, DBUS_TYPE_UINT32, &reason, DBUS_TYPE_INVALID)) {
+ pa_log_error("Failed to parse org.freedesktop.Notifications.NotificationClosed: %s.", err.message);
+ goto finish;
+ }
+
+ /* The assumption here is that if an action was invoked, it will have
+ already been processed since the ActionInvoked signal was emitted
+ first. That might not always be the case. */
+ PA_HASHMAP_FOREACH_KEY(id, notification, u->displaying, state) {
+ if (*id == dbus_notification_id) {
+ switch(reason) {
+ case 1: /* expired */
+ notification->handle_reply(pa_ui_notification_reply_new(PA_UI_NOTIFCATION_REPLY_EXPIRED, notification, NULL));
+ break;
+
+ case 2: /* dismissed */
+ /* what if ActionInvoked emitted after NotificationClosed */
+ notification->handle_reply(pa_ui_notification_reply_new(PA_UI_NOTIFCATION_REPLY_DISMISSED, notification, NULL));
+ break;
+
+ case 3: /* CloseNotification */
+ /* handled when CloseNotification was called */
+ break;
+
+ case 4: /* undefined/reserved */
+ default:
+ notification->handle_reply(pa_ui_notification_reply_new(PA_UI_NOTIFCATION_REPLY_ERROR, notification, NULL));
+ break;
+ }
+
+ pa_xfree(id);
+ pa_hashmap_remove(u->displaying, notification);
+
+ break;
+ }
+ }
+ } else if (dbus_message_is_signal(msg, "org.freedesktop.Notifications", "ActionInvoked")) {
+ if (!dbus_message_get_args(msg, &err, DBUS_TYPE_UINT32, &dbus_notification_id, DBUS_TYPE_STRING, &action_key, DBUS_TYPE_INVALID)) {
+ pa_log_error("Failed to parse org.freedesktop.Notifications.ActionInvoked: %s.", err.message);
+ goto finish;
+ }
+
+ /* pa_log_debug("org.freedesktop.Notifications.ActionInvoked(%u, %s)", dbus_notification_id, action_key); */
+ PA_HASHMAP_FOREACH_KEY(id, notification, u->displaying, state) {
+ if (*id == dbus_notification_id) {
+ notification->handle_reply(pa_ui_notification_reply_new(PA_UI_NOTIFCATION_REPLY_ACTION_INVOKED, notification, action_key));
+ }
+
+ pa_xfree(id);
+ pa_hashmap_remove(u->displaying, notification);
+ }
+ }
+
+finish:
+ dbus_error_free(&err);
+
+ return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+}
+
int pa__init(pa_module*m) {
DBusError err;
pa_ui_notification_backend *backend;
@@ -252,7 +352,7 @@ int pa__init(pa_module*m) {
pa_assert(m);
- backend = pa_xnew(pa_ui_notification_backend, 1);
+ backend = pa_xnew0(pa_ui_notification_backend, 1);
backend->userdata = u = pa_xnew(struct backend_userdata, 1);
dbus_error_init(&err);
@@ -266,7 +366,6 @@ int pa__init(pa_module*m) {
/* TODO: error checking */
PA_LLIST_HEAD_INIT(pa_dbus_pending, u->pending_send);
- PA_LLIST_HEAD_INIT(pa_dbus_pending, u->pending_cancel);
backend->send_notification = send_notification;
backend->cancel_notification = cancel_notification;
@@ -277,12 +376,24 @@ int pa__init(pa_module*m) {
mu->manager = manager = pa_ui_notification_manager_get(m->core);
mu->backend = backend;
+ if (!dbus_connection_add_filter(pa_dbus_connection_get(u->conn), signal_cb, backend, NULL)) {
+ pa_log_error("Failed to add filter function.");
+ goto fail;
+ }
+ u->filter_set = true;
+
+ if (pa_dbus_add_matches(pa_dbus_connection_get(u->conn), &err, "type='signal',sender='org.freedesktop.Notifications',interface='org.freedesktop.Notifications',path='/org/freedesktop/Notifications'", NULL) < 0) {
+ pa_log("Failed to add D-Bus matches: %s", err.message);
+ goto fail;
+ }
+
+
if(pa_ui_notification_manager_register_backend(manager, backend) >= 0)
return 0;
- else {
- pa__done(m);
- return -1;
- }
+
+fail:
+ pa__done(m);
+ return -1;
}
static void displaying_notifications_cancel(pa_ui_notification_backend *backend) {
@@ -303,48 +414,55 @@ static void displaying_notifications_cancel(pa_ui_notification_backend *backend)
static void pending_notifications_cancel(pa_dbus_pending **p) {
pa_dbus_pending *i;
+ pa_ui_notification *notification;
pa_assert(p);
while ((i = *p)) {
PA_LLIST_REMOVE(pa_dbus_pending, *p, i);
- /* TODO: notification reply cancel */
+ notification = i->call_data;
+ notification->handle_reply(pa_ui_notification_reply_new(PA_UI_NOTIFCATION_REPLY_CANCELLED, notification, NULL));
pa_dbus_pending_free(i);
}
}
void pa__done(pa_module*m) {
- pa_ui_notification_backend *b;
+ pa_ui_notification_backend *backend;
struct backend_userdata *u;
struct module_userdata *mu;
pa_assert(m);
- b = NULL;
+ backend = NULL;
if((mu = m->userdata)) {
- if((b = pa_ui_notification_manager_get_backend(mu->manager)) == mu->backend)
+ if((backend = pa_ui_notification_manager_get_backend(mu->manager)) == mu->backend)
pa_ui_notification_manager_unregister_backend(mu->manager);
pa_ui_notification_manager_unref(mu->manager);
}
- if(b) {
- u = b->userdata;
+ if(backend) {
+ u = backend->userdata;
if(u) {
pa_dbus_connection_unref(u->conn);
- displaying_notifications_cancel(b);
+ displaying_notifications_cancel(backend);
pa_hashmap_free(u->displaying, NULL, NULL);
pa_idxset_free(u->cancelling, NULL, NULL);
pending_notifications_cancel(&u->pending_send);
- pending_notifications_cancel(&u->pending_cancel);
+
+ if(u->filter_set) {
+ dbus_connection_remove_filter(pa_dbus_connection_get(u->conn), signal_cb, backend);
+ }
+
+ pa_dbus_remove_matches(pa_dbus_connection_get(u->conn), "type='signal',sender='org.freedesktop.Notifications',interface='org.freedesktop.Notifications',path='/org/freedesktop/Notifications'", NULL);
}
pa_xfree(u);
- pa_xfree(b);
+ pa_xfree(backend);
}
}
--
1.7.10.4
More information about the pulseaudio-discuss
mailing list