diff options
author | Ryan Lortie <desrt@desrt.ca> | 2011-01-02 14:05:38 -0500 |
---|---|---|
committer | Ryan Lortie <desrt@desrt.ca> | 2011-01-02 14:05:38 -0500 |
commit | 5ee7a71d40a98b46233b218d22d07fe850a02aef (patch) | |
tree | c0c2f34ed93047aa8518c2bffcbd2b64e7d35b40 /dbus-1 | |
parent | b8b4df32ac9c1325cd0d5c1a67a755b9f24180b6 (diff) | |
download | dconf-5ee7a71d40a98b46233b218d22d07fe850a02aef.tar.gz |
dbus-1: add support for watching keys
Add code to the test case to ensure that it's working.
Diffstat (limited to 'dbus-1')
-rw-r--r-- | dbus-1/dconf-dbus-1.c | 216 | ||||
-rw-r--r-- | dbus-1/dconf-dbus-1.h | 17 |
2 files changed, 153 insertions, 80 deletions
diff --git a/dbus-1/dconf-dbus-1.c b/dbus-1/dconf-dbus-1.c index c3a75bc..7aafc05 100644 --- a/dbus-1/dconf-dbus-1.c +++ b/dbus-1/dconf-dbus-1.c @@ -29,6 +29,7 @@ struct _DConfDBusClient { DBusConnection *session_bus; DBusConnection *system_bus; + GSList *watches; guint session_filter; guint system_filter; gint ref_count; @@ -162,48 +163,6 @@ dconf_dbus_client_add_value_to_iter (DBusMessageIter *iter, } } -#if 0 -static void -dconf_settings_backend_signal (DBusConnection *connection, - const gchar *sender_name, - const gchar *object_path, - const gchar *interface_name, - const gchar *signal_name, - GVariant *parameters, - gpointer user_data) -{ - DConfDBusClient *dcdbc = user_data; - const gchar *anti_expose; - const gchar **rels; - const gchar *path; - gchar bus_type; - - if (connection == dcdbc->session_bus) - { - anti_expose = dcdbc->anti_expose_tag; - bus_type = 'e'; - } - - else if (connection == dcdbc->system_bus) - { - anti_expose = NULL; - bus_type = 'y'; - } - - else - g_assert_not_reached (); - - if (dconf_engine_decode_notify (dcdbc->engine, anti_expose, - &path, &rels, bus_type, - sender_name, interface_name, - signal_name, parameters)) - { - /** XXX EMIT CHANGES XXX **/ - g_free (rels); - } -} -#endif - static void dconf_settings_backend_send (DConfDBusClient *dcdbc, DConfEngineMessage *dcem, @@ -460,6 +419,43 @@ dconf_settings_backend_scan_outstanding (DConfDBusClient *backend, return found; } +/* Watches are reference counted because they can be held both by the + * list of watches and by the pending watch registration. In the normal + * case, the registration completes before the watch is unsubscribed + * from but it might be the case that the watch is unsubscribed from + * before the AddMatch completes. For that reason, either thing could + * be responsible for freeing the watch structure; we solve that + * ambiguity using a reference count. + * + * We just initially set it to 2, since these are the only two users. + * That way we can skip having the ref() function. + */ +typedef struct +{ + DConfDBusClient *dcdbc; + gchar *name; + DConfDBusNotify notify; + gpointer user_data; + guint64 initial_state; + gint ref_count; +} Watch; + + + +static void +dconf_dbus_emit_change (DConfDBusClient *dcdbc, + const gchar *key) +{ + GSList *iter; + + for (iter = dcdbc->watches; iter; iter = iter->next) + { + Watch *watch = iter->data; + + if (g_str_has_prefix (key, watch->name)) + watch->notify (dcdbc, key, watch->user_data); + } +} GVariant * dconf_dbus_client_read (DConfDBusClient *dcdbc, @@ -484,46 +480,49 @@ dconf_dbus_client_write (DConfDBusClient *dcdbc, return FALSE; dconf_settings_backend_queue (dcdbc, &dcem, key, value, NULL); - /* XXX emit */ + dconf_dbus_emit_change (dcdbc, key); return TRUE; } -typedef struct -{ - DConfDBusClient *dcdbc; - guint64 state; - gchar name[1]; -} OutstandingWatch; - -static OutstandingWatch * -outstanding_watch_new (DConfDBusClient *dcdbc, - const gchar *name) +static Watch * +watch_new (DConfDBusClient *dcdbc, + const gchar *name, + DConfDBusNotify notify, + gpointer user_data) { - OutstandingWatch *watch; - gsize length; + Watch *watch; - length = strlen (name); - watch = g_malloc (G_STRUCT_OFFSET (OutstandingWatch, name) + length + 1); + watch = g_slice_new (Watch); watch->dcdbc = dconf_dbus_client_ref (dcdbc); - watch->state = dconf_engine_get_state (dcdbc->engine); - strcpy (watch->name, name); + watch->user_data = user_data; + watch->name = g_strdup (name); + watch->notify = notify; + + watch->initial_state = dconf_engine_get_state (dcdbc->engine); + watch->ref_count = 2; + + dcdbc->watches = g_slist_prepend (dcdbc->watches, watch); return watch; } static void -outstanding_watch_free (OutstandingWatch *watch) +watch_unref (Watch *watch) { - dconf_dbus_client_unref (watch->dcdbc); - g_free (watch); + if (--watch->ref_count == 0) + { + dconf_dbus_client_unref (watch->dcdbc); + g_free (watch->name); + g_slice_free (Watch, watch); + } } static void add_match_done (DBusPendingCall *pending, gpointer user_data) { - OutstandingWatch *watch = user_data; + Watch *watch = user_data; GError *error = NULL; GVariant *reply; @@ -531,10 +530,16 @@ add_match_done (DBusPendingCall *pending, if (reply == NULL) { + /* This is extremely unlikely to happen and it happens + * asynchronous to the user's call. Since the user doesn't know + * that it happened, we pretend that it didn't (ie: we leave the + * watch structure in the list). + */ + g_critical ("DBus AddMatch for dconf path '%s': %s", watch->name, error->message); - outstanding_watch_free (watch); g_error_free (error); + watch_unref (watch); return; } @@ -542,27 +547,29 @@ add_match_done (DBusPendingCall *pending, else g_variant_unref (reply); /* it is just an empty tuple */ - /* In the normal case we can just free everything and be done. + /* In the normal case we're done. * * There is a fleeting chance, however, that the database has changed * in the meantime. In that case we can only assume that the subject * of this watch changed in that time period and emit a signal to that * effect. */ - if (dconf_engine_get_state (watch->dcdbc->engine) != watch->state) - /* XXX emit watch->name */ + if (dconf_engine_get_state (watch->dcdbc->engine) != watch->initial_state) + watch->notify (watch->dcdbc, watch->name, watch->user_data); - outstanding_watch_free (watch); + watch_unref (watch); } void dconf_dbus_client_subscribe (DConfDBusClient *dcdbc, - const gchar *name) + const gchar *name, + DConfDBusNotify notify, + gpointer user_data) { - OutstandingWatch *watch; DConfEngineMessage dcem; + Watch *watch; - watch = outstanding_watch_new (dcdbc, name); + watch = watch_new (dcdbc, name, notify, user_data); dconf_engine_watch (dcdbc->engine, name, &dcem); dconf_settings_backend_send (dcdbc, &dcem, add_match_done, watch); dconf_engine_message_destroy (&dcem); @@ -570,13 +577,30 @@ dconf_dbus_client_subscribe (DConfDBusClient *dcdbc, void dconf_dbus_client_unsubscribe (DConfDBusClient *dcdbc, - const gchar *name) + DConfDBusNotify notify, + gpointer user_data) { DConfEngineMessage dcem; + GSList **ptr; - dconf_engine_unwatch (dcdbc->engine, name, &dcem); - dconf_settings_backend_send (dcdbc, &dcem, NULL, NULL); - dconf_engine_message_destroy (&dcem); + for (ptr = &dcdbc->watches; *ptr; ptr = &(*ptr)->next) + { + Watch *watch = (*ptr)->data; + + if (watch->notify == notify && watch->user_data == user_data) + { + *ptr = g_slist_remove (*ptr, *ptr); + + dconf_engine_unwatch (dcdbc->engine, watch->name, &dcem); + dconf_settings_backend_send (dcdbc, &dcem, NULL, NULL); + dconf_engine_message_destroy (&dcem); + watch_unref (watch); + + return; + } + } + + g_warning ("No matching watch found to unsubscribe"); } gboolean @@ -591,6 +615,41 @@ dconf_settings_backend_service_func (DConfEngineMessage *dcem) g_assert_not_reached (); } +static DBusHandlerResult +dconf_dbus_client_filter (DBusConnection *connection, + DBusMessage *message, + void *user_data) +{ + DConfDBusClient *dcdbc = user_data; + + if (dbus_message_is_signal (message, "ca.desrt.dconf.Writer", "Notify") && + dbus_message_has_signature (message, "sass")) + { + DBusMessageIter iter, sub; + const gchar *path, *tag; + + dbus_message_iter_init (message, &iter); + dbus_message_iter_get_basic (&iter, &path); + dbus_message_iter_recurse (&iter, &sub); + dbus_message_iter_get_basic (&iter, &tag); + + /* XXX todo: anti-expose */ + + while (dbus_message_iter_get_arg_type (&sub) == 's') + { + const gchar *item; + gchar *full; + + dbus_message_iter_get_basic (&iter, &item); + full = g_strconcat (path, item, NULL); + dconf_dbus_emit_change (dcdbc, full); + g_free (full); + } + } + + return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; +} + DConfDBusClient * dconf_dbus_client_new (const gchar *profile, DBusConnection *system, @@ -606,6 +665,9 @@ dconf_dbus_client_new (const gchar *profile, dcdbc->session_bus = dbus_connection_ref (session); dcdbc->ref_count = 1; + dbus_connection_add_filter (system, dconf_dbus_client_filter, dcdbc, NULL); + dbus_connection_add_filter (session, dconf_dbus_client_filter, dcdbc, NULL); + return dcdbc; } @@ -614,6 +676,10 @@ dconf_dbus_client_unref (DConfDBusClient *dcdbc) { if (--dcdbc->ref_count == 0) { + dbus_connection_remove_filter (dcdbc->session_bus, + dconf_dbus_client_filter, dcdbc); + dbus_connection_remove_filter (dcdbc->system_bus, + dconf_dbus_client_filter, dcdbc); dbus_connection_unref (dcdbc->session_bus); dbus_connection_unref (dcdbc->system_bus); diff --git a/dbus-1/dconf-dbus-1.h b/dbus-1/dconf-dbus-1.h index d137fc8..649f24d 100644 --- a/dbus-1/dconf-dbus-1.h +++ b/dbus-1/dconf-dbus-1.h @@ -25,9 +25,13 @@ typedef struct _DConfDBusClient DConfDBusClient; -DConfDBusClient * dconf_dbus_client_new (const gchar *profile, - DBusConnection *system, - DBusConnection *session); +typedef void (* DConfDBusNotify) (DConfDBusClient *dcdbc, + const gchar *key, + gpointer user_data); + +DConfDBusClient * dconf_dbus_client_new (const gchar *profile, + DBusConnection *system, + DBusConnection *session); void dconf_dbus_client_unref (DConfDBusClient *dcdbc); DConfDBusClient * dconf_dbus_client_ref (DConfDBusClient *dcdbc); @@ -37,9 +41,12 @@ gboolean dconf_dbus_client_write (DConfDB const gchar *key, GVariant *value); void dconf_dbus_client_subscribe (DConfDBusClient *dcdbc, - const gchar *name); + const gchar *name, + DConfDBusNotify notify, + gpointer user_data); void dconf_dbus_client_unsubscribe (DConfDBusClient *dcdbc, - const gchar *name); + DConfDBusNotify notify, + gpointer user_data); gboolean dconf_dbus_client_has_pending (DConfDBusClient *dcdbc); #endif /* _dconf_dbus_1_h_ */ |