[tracker/gconf-dbus: 4/15] libtracker-common: Listen for changes on GConf using DBus directly for meegotouch locales



commit dc00c29e8d92a84fcee5b1e01317bf0ddee8a29c
Author: Philip Van Hoof <philip codeminded be>
Date:   Mon Apr 4 12:23:09 2011 +0200

    libtracker-common: Listen for changes on GConf using DBus directly for meegotouch locales

 src/libtracker-common/tracker-locale-gconfdbus.c |  334 +++++++++++++++++++++-
 src/libtracker-common/tracker-locale-gconfdbus.h |    6 +
 src/libtracker-common/tracker-locale.c           |   10 +
 3 files changed, 341 insertions(+), 9 deletions(-)
---
diff --git a/src/libtracker-common/tracker-locale-gconfdbus.c b/src/libtracker-common/tracker-locale-gconfdbus.c
index 538d66d..ea54191 100644
--- a/src/libtracker-common/tracker-locale-gconfdbus.c
+++ b/src/libtracker-common/tracker-locale-gconfdbus.c
@@ -46,6 +46,11 @@
 
 static gchar*gconf_dbus_default_db = NULL;
 static GDBusConnection *connection = NULL;
+static gboolean service_running = FALSE;
+static guint watch_name_id = 0;
+static guint registration_id = 0;
+static GStaticMutex subscribers_mutex = G_STATIC_MUTEX_INIT;
+GDBusNodeInfo *introspection_data = NULL;
 
 /* gconf keys for tracker locales, as defined in:
  * http://apidocs.meego.com/1.0/mtf/i18n.html
@@ -58,6 +63,157 @@ static const gchar *gconf_locales[TRACKER_LOCALE_LAST] = {
 	MEEGOTOUCH_LOCALE_DIR "/lc_monetary"
 };
 
+/* Structure to hold the notification data of each subscriber */
+typedef struct {
+	TrackerLocaleID id;
+	TrackerLocaleNotifyFunc func;
+	gpointer user_data;
+	GFreeFunc destroy_notify;
+} TrackerLocaleNotification;
+
+/* List of subscribers which want to get notified of locale changes */
+static GSList *subscribers;
+
+static const gchar introspection_xml[] =
+  "<node>"
+  "  <interface name='" GCONF_DBUS_CLIENT_INTERFACE "'>"
+  "    <method name='Notify'>"
+  "      <arg type='s' name='database' direction='in' />"
+  "      <arg type='s' name='namespace_section' direction='in' />"
+  "      <arg type='(s(is)bsbb)' name='value' direction='in' />"
+  "    </method>"
+  "  </interface>"
+  "</node>";
+
+static gboolean
+add_notify (void)
+{
+	GVariant *reply;
+	GError *error = NULL;
+
+	reply = g_dbus_connection_call_sync (connection,
+	                                     GCONF_DBUS_SERVICE,
+	                                     gconf_dbus_default_db,
+	                                     GCONF_DBUS_DATABASE_INTERFACE,
+	                                     "AddNotify",
+	                                     g_variant_new ("(s)", MEEGOTOUCH_LOCALE_DIR),
+	                                     NULL,
+	                                     G_DBUS_CALL_FLAGS_NONE,
+	                                     -1,
+	                                     NULL,
+	                                     &error);
+
+	if (error) {
+		g_critical ("%s", error->message);
+		g_clear_error (&error);
+		return FALSE;
+	}
+
+	g_variant_unref (reply);
+
+	return TRUE;
+}
+
+static void
+handle_method_call (GDBusConnection       *connection,
+                    const gchar           *sender,
+                    const gchar           *object_path,
+                    const gchar           *interface_name,
+                    const gchar           *method_name,
+                    GVariant              *parameters,
+                    GDBusMethodInvocation *invocation,
+                    gpointer               user_data)
+{
+	/* Takes place in mainloop */
+
+	if (g_strcmp0 (method_name, "Notify") == 0) {
+		const gchar *key = NULL, *value = NULL;
+		const gchar *schema = NULL, *database = NULL;
+		const gchar *namespace_name = NULL;
+		gboolean is_set, is_default, is_writable;
+		gint type, i;
+		GSList *li;
+
+		if (g_variant_is_of_type (parameters, G_VARIANT_TYPE ("(ss(s(is)bsbb))"))) {
+			g_variant_get (parameters, "(&s&s(&s(i&s)b&sbb))",
+			               &database, &namespace_name,
+			               &key, &type, &value,
+			               &is_set, &schema,
+			               &is_default, &is_writable,
+			               NULL);
+
+			/* Find the proper locale to change */
+			for (i = 0; i < TRACKER_LOCALE_LAST; i++) {
+				if (strcmp (gconf_locales[i], key) == 0) {
+					break;
+				}
+			}
+
+			/* Oh, not found? */
+			if (i == TRACKER_LOCALE_LAST) {
+				g_debug ("Skipping change on gconf key '%s' as not really needed", key);
+				return;
+			}
+
+			/* Ensure a proper value was set */
+			if (value == NULL || value[0] == '\0') {
+				g_warning ("Locale value for '%s' cannot be NULL, not changing %s",
+				           gconf_locales[i],
+				           tracker_locale_get_name (i));
+				return;
+			}
+
+			/* This always runs from mainloop, so no need for a lock other than
+			 * subscribers, which might be added and removed by threads (and are
+			 * here executed by function pointer on the mainloop) */
+
+			tracker_locale_set (i, value);
+
+			g_static_mutex_lock (&subscribers_mutex);
+
+			for (li = subscribers; li; li = g_slist_next (li)) {
+				TrackerLocaleNotification *data = li->data;
+
+				if (i == data->id) {
+					g_debug ("Notifying locale '%s' change to subscriber '%p'",
+					         tracker_locale_get_name(i),
+					         data);
+					data->func (i, data->user_data);
+				}
+			}
+
+			g_static_mutex_unlock (&subscribers_mutex);
+		}
+	}
+}
+
+static GVariant *
+handle_get_property (GDBusConnection  *connection,
+                     const gchar      *sender,
+                     const gchar      *object_path,
+                     const gchar      *interface_name,
+                     const gchar      *property_name,
+                     GError          **error,
+                     gpointer          user_data)
+{
+	return NULL;
+}
+
+static gboolean
+handle_set_property (GDBusConnection  *connection,
+                     const gchar      *sender,
+                     const gchar      *object_path,
+                     const gchar      *interface_name,
+                     const gchar      *property_name,
+                     GVariant         *value,
+                     GError          **error,
+                     gpointer          user_data)
+{
+	return TRUE;
+}
+
+
+
 static gchar *
 get_value_from_config (const gchar *key_in)
 {
@@ -104,13 +260,49 @@ get_value_from_config (const gchar *key_in)
 	return val;
 }
 
+
+static void
+on_gconfd_dbus_appeared (GDBusConnection *connection,
+                         const gchar     *name,
+                         const gchar     *name_owner,
+                         gpointer         user_data)
+{
+	guint i;
+
+	service_running = TRUE;
+	add_notify ();
+
+	/* And (re)initialize all */
+	for (i = 0; i < TRACKER_LOCALE_LAST; i++) {
+		gchar *str;
+
+		str = get_value_from_config (gconf_locales[i]);
+		if (str) {
+			tracker_locale_set (i, str);
+			g_free (str);
+		}
+	}
+}
+
+static void
+on_gconfd_dbus_disappeared  (GDBusConnection *connection,
+                             const gchar     *name,
+                             gpointer         user_data)
+{
+	service_running = FALSE;
+}
+
 void
 tracker_locale_gconfdbus_init (void)
 {
 	if (!g_getenv (TRACKER_DISABLE_MEEGOTOUCH_LOCALE_ENV)) {
-		guint i;
 		GError *error = NULL;
 		GVariant *reply;
+		GDBusInterfaceVTable interface_vtable = {
+			handle_method_call,
+			handle_get_property,
+			handle_set_property
+		};
 
 		g_message ("Retrieving locale from GConf is ENABLED");
 
@@ -122,6 +314,8 @@ tracker_locale_gconfdbus_init (void)
 			return;
 		}
 
+		service_running = TRUE;
+
 		reply = g_dbus_connection_call_sync (connection,
 		                                     GCONF_DBUS_SERVICE,
 		                                     GCONF_DBUS_SERVER_OBJECT,
@@ -145,28 +339,150 @@ tracker_locale_gconfdbus_init (void)
 
 		g_variant_unref (reply);
 
-		/* And initialize all */
-		for (i = 0; i < TRACKER_LOCALE_LAST; i++) {
-			gchar *str;
+		introspection_data = g_dbus_node_info_new_for_xml (introspection_xml, &error);
 
-			str = get_value_from_config (gconf_locales[i]);
-			if (str) {
-				tracker_locale_set (i, str);
-				g_free (str);
-			}
+		if (error) {
+			g_critical ("%s", error->message);
+			g_clear_error (&error);
+			return;
+		}
+
+		registration_id =
+			g_dbus_connection_register_object (connection,
+			                                   GCONF_DBUS_CLIENT_OBJECT,
+			                                   introspection_data->interfaces[0],
+			                                   &interface_vtable,
+			                                   NULL,
+			                                   NULL,
+			                                   &error);
+
+		if (error) {
+			g_critical ("%s", error->message);
+			g_clear_error (&error);
+			return;
 		}
+
+		watch_name_id = g_bus_watch_name_on_connection (connection,
+		                                                GCONF_DBUS_SERVICE,
+		                                                G_BUS_NAME_WATCHER_FLAGS_NONE,
+		                                                on_gconfd_dbus_appeared,
+		                                                on_gconfd_dbus_disappeared,
+		                                                NULL, NULL);
 	}
 }
 
 void
 tracker_locale_gconfdbus_shutdown (void)
 {
+	if (gconf_dbus_default_db != NULL && connection != NULL) {
+		GVariant *reply;
+		GError *error = NULL;
+
+		reply = g_dbus_connection_call_sync (connection,
+		                                     GCONF_DBUS_SERVICE,
+		                                     gconf_dbus_default_db,
+		                                     GCONF_DBUS_DATABASE_INTERFACE,
+		                                     "RemoveNotify",
+		                                     g_variant_new ("(s)", MEEGOTOUCH_LOCALE_DIR),
+		                                     NULL,
+		                                     G_DBUS_CALL_FLAGS_NONE,
+		                                     -1,
+		                                     NULL,
+		                                     &error);
+
+		if (error) {
+			g_warning ("%s", error->message);
+			g_clear_error (&error);
+		} else {
+			g_variant_unref (reply);
+		}
+	}
+
+	if (watch_name_id != 0) {
+		g_bus_unwatch_name (watch_name_id);
+		watch_name_id = 0;
+	}
+
+	if (registration_id != 0) {
+		g_dbus_connection_unregister_object (connection, registration_id);
+		registration_id = 0;
+	}
+
 	g_free (gconf_dbus_default_db);
 	gconf_dbus_default_db = NULL;
 
+	if (introspection_data) {
+		g_dbus_node_info_unref (introspection_data);
+		introspection_data = NULL;
+	}
+
 	if (connection) {
 		g_object_unref (connection);
 		connection = NULL;
 	}
 }
 
+gpointer
+tracker_locale_gconfdbus_notify_add (TrackerLocaleID         id,
+                                     TrackerLocaleNotifyFunc func,
+                                     gpointer                user_data,
+                                     GFreeFunc               destroy_notify)
+{
+	TrackerLocaleNotification *data;
+
+	/* Can be called from a thread */
+
+	g_assert (func != NULL);
+
+	data = g_slice_new (TrackerLocaleNotification);
+	data->id = id;
+	data->func = func;
+	data->user_data = user_data;
+	data->destroy_notify = destroy_notify;
+
+	g_static_mutex_lock (&subscribers_mutex);
+	subscribers = g_slist_prepend (subscribers, data);
+	g_static_mutex_unlock (&subscribers_mutex);
+
+	return data;
+}
+
+static gboolean
+destroy_locale_notify (gpointer data_p)
+{
+	/* Always on mainloop */
+
+	TrackerLocaleNotification *data = data_p;
+
+	/* Call the provided destroy_notify if any. */
+	if (data->destroy_notify) {
+		data->destroy_notify (data->user_data);
+	}
+
+	/* And fully dispose the notification data */
+	g_slice_free (TrackerLocaleNotification, data);
+
+	return FALSE;
+}
+
+void
+tracker_locale_gconfdbus_notify_remove (gpointer notification_id)
+{
+	GSList *li;
+
+	/* Can be called from a thread */
+
+	g_static_mutex_lock (&subscribers_mutex);
+
+	li = g_slist_find (subscribers, notification_id);
+	if (li) {
+		TrackerLocaleNotification *data = li->data;
+
+		/* Remove item from list of subscribers */
+		subscribers = g_slist_delete_link (subscribers, li);
+
+		g_idle_add (destroy_locale_notify, data);
+	}
+
+	g_static_mutex_unlock (&subscribers_mutex);
+}
diff --git a/src/libtracker-common/tracker-locale-gconfdbus.h b/src/libtracker-common/tracker-locale-gconfdbus.h
index 0ca2691..130a0c0 100644
--- a/src/libtracker-common/tracker-locale-gconfdbus.h
+++ b/src/libtracker-common/tracker-locale-gconfdbus.h
@@ -33,6 +33,12 @@ G_BEGIN_DECLS
 void tracker_locale_gconfdbus_init     (void);
 void tracker_locale_gconfdbus_shutdown (void);
 
+gpointer tracker_locale_gconfdbus_notify_add    (TrackerLocaleID         id,
+                                                 TrackerLocaleNotifyFunc func,
+                                                 gpointer                user_data,
+                                                 GFreeFunc               destroy_notify);
+void     tracker_locale_gconfdbus_notify_remove (gpointer                notification_id);
+
 G_END_DECLS
 
 #endif /* __LIBTRACKER_COMMON_LOCALE_GCONFDBUS_H__ */
diff --git a/src/libtracker-common/tracker-locale.c b/src/libtracker-common/tracker-locale.c
index 82058aa..f2e91fa 100644
--- a/src/libtracker-common/tracker-locale.c
+++ b/src/libtracker-common/tracker-locale.c
@@ -178,10 +178,20 @@ tracker_locale_notify_add (TrackerLocaleID         id,
                            gpointer                user_data,
                            GFreeFunc               destroy_notify)
 {
+#ifdef HAVE_MAEMO
+	return tracker_locale_gconfdbus_notify_add (id, func, user_data, destroy_notify);
+#else
+	/* If not using gconf locales, this is a no-op... */
 	return NULL;
+#endif /* HAVE_MAEMO */
 }
 
 void
 tracker_locale_notify_remove (gpointer notification_id)
 {
+#ifdef HAVE_MAEMO
+	return tracker_locale_gconfdbus_notify_remove (notification_id);
+#else
+	/* If not using gconf locales, this is a no-op... */
+#endif /* HAVE_MAEMO */
 }



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]