[gconf] [gsettings] Add untested notification support
- From: Vincent Untz <vuntz src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gconf] [gsettings] Add untested notification support
- Date: Fri, 16 Apr 2010 13:10:38 +0000 (UTC)
commit 7a795fcf4a50dcd305a6fc7fd8ec0dc0938ad727
Author: Vincent Untz <vuntz gnome org>
Date: Fri Apr 16 01:03:30 2010 -0400
[gsettings] Add untested notification support
gsettings/gconfsettingsbackend.c | 198 +++++++++++++++++++++++++++++++++++++-
1 files changed, 193 insertions(+), 5 deletions(-)
---
diff --git a/gsettings/gconfsettingsbackend.c b/gsettings/gconfsettingsbackend.c
index 400f6d2..7cae64b 100644
--- a/gsettings/gconfsettingsbackend.c
+++ b/gsettings/gconfsettingsbackend.c
@@ -31,9 +31,12 @@
G_DEFINE_DYNAMIC_TYPE (GConfSettingsBackend, gconf_settings_backend, G_TYPE_SETTINGS_BACKEND);
+typedef struct _GConfSettingsBackendNotifier GConfSettingsBackendNotifier;
+
struct _GConfSettingsBackendPrivate
{
GConfClient *client;
+ GList *notifiers;
/* By definition, with GSettings, we can't write to a key if we're not
* subscribed to it or its parent. This means we'll be monitoring it, and
* that we'll get a change notification for the write. That's something that
@@ -41,6 +44,22 @@ struct _GConfSettingsBackendPrivate
GHashTable *ignore_notifications;
};
+/* The rationale behind the non-trivial handling of notifiers here is that we
+ * only want to receive one notification when a key changes. The naive approach
+ * would be to register one notifier per subscribed path, but in gconf, we get
+ * notificiations for all keys living below the path. So subscribing to
+ * /apps/panel and /apps/panel/general will lead to two notifications for a key
+ * living under /apps/panel/general. We want to avoid that, so we will only
+ * have a notifier for /apps/panel in such a case. */
+struct _GConfSettingsBackendNotifier
+{
+ GConfSettingsBackendNotifier *parent;
+ gchar *path;
+ guint refcount;
+ guint notify_id;
+ GList *subpaths;
+};
+
static gboolean
gconf_settings_backend_simple_gconf_value_type_is_compatible (GConfValueType type,
const GVariantType *expected_type)
@@ -595,7 +614,6 @@ gconf_settings_backend_get_gconf_path_from_name (const gchar *name)
return g_strndup (name, strlen(name) - 1);
}
-#if 0
static void
gconf_settings_backend_notified (GConfClient *client,
guint cnxn_id,
@@ -611,7 +629,171 @@ gconf_settings_backend_notified (GConfClient *client,
g_settings_backend_changed (G_SETTINGS_BACKEND (gconf), entry->key, NULL);
}
-#endif
+
+static GConfSettingsBackendNotifier *
+gconf_settings_backend_find_notifier_or_parent (GConfSettingsBackend *gconf,
+ const gchar *path)
+{
+ GConfSettingsBackendNotifier *parent;
+ GList *l;
+
+ l = gconf->priv->notifiers;
+ parent = NULL;
+
+ while (l != NULL)
+ {
+ GConfSettingsBackendNotifier *notifier;
+ notifier = l->data;
+ if (g_str_equal (path, notifier->path))
+ return notifier;
+ if (g_str_has_prefix (path, notifier->path))
+ {
+ parent = notifier;
+ l = parent->subpaths;
+ continue;
+ }
+ if (g_str_has_prefix (notifier->path, path))
+ break;
+
+ l = l->next;
+ }
+
+ return parent;
+}
+
+static void
+gconf_settings_backend_free_notifier (GConfSettingsBackendNotifier *notifier,
+ GConfSettingsBackend *gconf)
+{
+ if (notifier->path)
+ g_free (notifier->path);
+ notifier->path = NULL;
+
+ if (notifier->notify_id)
+ gconf_client_notify_remove (gconf->priv->client, notifier->notify_id);
+ notifier->notify_id = 0;
+
+ g_list_foreach (notifier->subpaths, (GFunc) gconf_settings_backend_free_notifier, gconf);
+ g_list_free (notifier->subpaths);
+ notifier->subpaths = NULL;
+
+ g_slice_free (GConfSettingsBackendNotifier, notifier);
+}
+
+/* Returns: TRUE if the notifier was created, FALSE if it was already existing. */
+static gboolean
+gconf_settings_backend_add_notifier (GConfSettingsBackend *gconf,
+ const gchar *path)
+{
+ GConfSettingsBackendNotifier *n_or_p;
+ GConfSettingsBackendNotifier *notifier;
+ GList *siblings;
+ GList *l;
+
+ n_or_p = gconf_settings_backend_find_notifier_or_parent (gconf, path);
+
+ if (n_or_p && g_str_equal (path, n_or_p->path))
+ {
+ n_or_p->refcount += 1;
+ return FALSE;
+ }
+
+ notifier = g_slice_new0 (GConfSettingsBackendNotifier);
+ notifier->parent = n_or_p;
+ notifier->path = g_strdup (path);
+ notifier->refcount = 1;
+
+ if (notifier->parent == NULL)
+ notifier->notify_id = gconf_client_notify_add (gconf->priv->client, path,
+ (GConfClientNotifyFunc) gconf_settings_backend_notified, gconf,
+ NULL, NULL);
+ else
+ notifier->notify_id = 0;
+
+ /* Move notifiers living at the same level but that are subpaths below this
+ * new notifier, removing their notify handler if necessary. */
+ if (notifier->parent)
+ siblings = notifier->parent->subpaths;
+ else
+ siblings = gconf->priv->notifiers;
+
+ l = siblings;
+ while (l != NULL)
+ {
+ GConfSettingsBackendNotifier *sibling;
+ GList *next;
+
+ sibling = l->data;
+ next = l->next;
+
+ if (g_str_has_prefix (sibling->path, notifier->path))
+ {
+ if (sibling->notify_id)
+ {
+ gconf_client_notify_remove (gconf->priv->client,
+ sibling->notify_id);
+ sibling->notify_id = 0;
+ }
+
+ siblings = g_list_remove_link (siblings, l);
+ l->next = notifier->subpaths;
+ notifier->subpaths = l;
+ }
+
+ l = next;
+ }
+
+ if (notifier->parent)
+ notifier->parent->subpaths = siblings;
+ else
+ gconf->priv->notifiers = siblings;
+
+ return TRUE;
+}
+
+/* Returns: TRUE if the notifier was removed, FALSE if it is still referenced. */
+static gboolean
+gconf_settings_backend_remove_notifier (GConfSettingsBackend *gconf,
+ const gchar *path)
+{
+ GConfSettingsBackendNotifier *notifier;
+
+ notifier = gconf_settings_backend_find_notifier_or_parent (gconf, path);
+
+ g_assert (g_str_equal (path, notifier->path));
+
+ notifier->refcount -= 1;
+
+ if (notifier->refcount > 0)
+ return FALSE;
+
+ /* Move subpaths to the parent, and add a notify handler for each of them if
+ * they have no parent anymore. */
+ if (notifier->parent)
+ notifier->parent->subpaths = g_list_concat (notifier->parent->subpaths,
+ notifier->subpaths);
+ else
+ {
+ GList *l;
+
+ for (l = notifier->subpaths; l != NULL; l = l->next)
+ {
+ GConfSettingsBackendNotifier *child = l->data;
+ child->notify_id = gconf_client_notify_add (gconf->priv->client, child->path,
+ (GConfClientNotifyFunc) gconf_settings_backend_notified, gconf,
+ NULL, NULL);
+ }
+
+ gconf->priv->notifiers = g_list_concat (gconf->priv->notifiers,
+ notifier->subpaths);
+ }
+
+ notifier->subpaths = NULL;
+
+ gconf_settings_backend_free_notifier (notifier, gconf);
+
+ return TRUE;
+}
static void
gconf_settings_backend_subscribe (GSettingsBackend *backend,
@@ -621,9 +803,9 @@ gconf_settings_backend_subscribe (GSettingsBackend *backend,
gchar *path;
path = gconf_settings_backend_get_gconf_path_from_name (name);
- gconf_client_add_dir (gconf->priv->client, path, GCONF_CLIENT_PRELOAD_ONELEVEL, NULL);
+ if (gconf_settings_backend_add_notifier (gconf, path))
+ gconf_client_add_dir (gconf->priv->client, path, GCONF_CLIENT_PRELOAD_ONELEVEL, NULL);
g_free (path);
- //FIXME notify: we need to be careful: we only want to receive one notification per key
}
static void
@@ -634,7 +816,8 @@ gconf_settings_backend_unsubscribe (GSettingsBackend *backend,
gchar *path;
path = gconf_settings_backend_get_gconf_path_from_name (name);
- gconf_client_remove_dir (gconf->priv->client, path, NULL);
+ if (gconf_settings_backend_remove_notifier (gconf, path))
+ gconf_client_remove_dir (gconf->priv->client, path, NULL);
g_free (path);
}
@@ -649,6 +832,10 @@ gconf_settings_backend_finalize (GObject *object)
{
GConfSettingsBackend *gconf = GCONF_SETTINGS_BACKEND (object);
+ g_list_foreach (gconf->priv->notifiers, (GFunc) gconf_settings_backend_free_notifier, gconf);
+ g_list_free (gconf->priv->notifiers);
+ gconf->priv->notifiers = NULL;
+
g_object_unref (gconf->priv->client);
gconf->priv->client = NULL;
@@ -666,6 +853,7 @@ gconf_settings_backend_init (GConfSettingsBackend *gconf)
GCONF_TYPE_SETTINGS_BACKEND,
GConfSettingsBackendPrivate);
gconf->priv->client = gconf_client_get_default ();
+ gconf->priv->notifiers = NULL;
gconf->priv->ignore_notifications = g_hash_table_new_full (g_str_hash, g_str_equal,
g_free, NULL);
}
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]