[tracker/gconf-dbus] libtracker-common: Use GDBus manually for gconf-dbus, gconfclient isn't thread safe



commit e073eaa5d414f657efa13a06b41f2e06d1556842
Author: Philip Van Hoof <philip codeminded be>
Date:   Fri Apr 1 14:09:38 2011 +0200

    libtracker-common: Use GDBus manually for gconf-dbus, gconfclient isn't thread safe

 src/libtracker-common/Makefile.am                |    5 +
 src/libtracker-common/tracker-locale-gconfdbus.c |  483 ++++++++++++++++++++++
 src/libtracker-common/tracker-locale-gconfdbus.h |   44 ++
 src/libtracker-common/tracker-locale.c           |  275 ++-----------
 src/libtracker-common/tracker-locale.h           |   19 +-
 5 files changed, 567 insertions(+), 259 deletions(-)
---
diff --git a/src/libtracker-common/Makefile.am b/src/libtracker-common/Makefile.am
index 7c2c7c0..a5c5711 100644
--- a/src/libtracker-common/Makefile.am
+++ b/src/libtracker-common/Makefile.am
@@ -64,6 +64,11 @@ libtracker_common_la_SOURCES += tracker-language.c
 noinst_HEADERS += tracker-language.h
 endif
 
+if HAVE_MAEMO
+libtracker_common_la_SOURCES += tracker-locale-gconfdbus.c
+noinst_HEADERS += tracker-locale-gconfdbus.h
+endif
+
 libtracker_common_la_LDFLAGS = \
 	-version-info $(LT_CURRENT):$(LT_REVISION):$(LT_AGE)
 
diff --git a/src/libtracker-common/tracker-locale-gconfdbus.c b/src/libtracker-common/tracker-locale-gconfdbus.c
new file mode 100644
index 0000000..2b44663
--- /dev/null
+++ b/src/libtracker-common/tracker-locale-gconfdbus.c
@@ -0,0 +1,483 @@
+/*
+ * Copyright (C) 2010 Nokia <ivan frade nokia com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA  02110-1301, USA.
+ */
+
+#include "config.h"
+
+#include <locale.h>
+#include <string.h>
+
+#include <glib.h>
+#include <glib-object.h>
+#include <gio/gio.h>
+
+#include "tracker-locale-gconfdbus.h"
+
+#define GCONF_DBUS_SERVICE                    "org.gnome.GConfDBus"
+#define GCONF_DBUS_SERVER_INTERFACE           "org.gnome.GConfDBus.Server"
+#define GCONF_DBUS_DATABASE_INTERFACE         "org.gnome.GConfDBus.Database"
+#define GCONF_DBUS_SERVER_OBJECT              "/org/gnome/GConfDBus/Server"
+#define GCONF_DBUS_CLIENT_OBJECT              "/org/gnome/GConfDBus/Client"
+#define GCONF_DBUS_CLIENT_INTERFACE           "org.gnome.GConfDBus.Client"
+
+/* Base dir for all gconf locale values */
+#define MEEGOTOUCH_LOCALE_DIR                 "/meegotouch/i18n"
+
+#define TRACKER_DISABLE_MEEGOTOUCH_LOCALE_ENV "TRACKER_DISABLE_MEEGOTOUCH_LOCALE"
+
+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
+ */
+static const gchar *gconf_locales[TRACKER_LOCALE_LAST] = {
+	MEEGOTOUCH_LOCALE_DIR "/language",
+	MEEGOTOUCH_LOCALE_DIR "/lc_time",
+	MEEGOTOUCH_LOCALE_DIR "/lc_collate",
+	MEEGOTOUCH_LOCALE_DIR "/lc_numeric",
+	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, *value, *schema, *database, *namespace_name;
+		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));
+			}
+
+			/* 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 void
+on_gconfd_dbus_appeared (GDBusConnection *connection,
+                         const gchar     *name,
+                         const gchar     *name_owner,
+                         gpointer         user_data)
+{
+	service_running = TRUE;
+	add_notify ();
+}
+
+static void
+on_gconfd_dbus_disappeared  (GDBusConnection *connection,
+                             const gchar     *name,
+                             gpointer         user_data)
+{
+	service_running = FALSE;
+}
+
+
+static gchar *
+get_value_from_config (const gchar *key_in)
+{
+	const gchar *locale = setlocale (LC_CTYPE, NULL);
+	const gchar *key, *value, *schema;
+	gchar *val = NULL;
+	gboolean is_set, is_default, is_writable;
+	gint type;
+	GError *error = NULL;
+	GVariant *reply;
+
+	reply = g_dbus_connection_call_sync (connection,
+	                                     GCONF_DBUS_SERVICE,
+	                                     gconf_dbus_default_db,
+	                                     GCONF_DBUS_DATABASE_INTERFACE,
+	                                     "LookupExtended",
+	                                     g_variant_new ("(ssb)", key_in, locale, TRUE),
+	                                     NULL,
+	                                     G_DBUS_CALL_FLAGS_NONE,
+	                                     -1,
+	                                     NULL,
+	                                     &error);
+
+	if (error) {
+		g_variant_unref (reply);
+		g_critical ("%s", error->message);
+		g_clear_error (&error);
+
+		return NULL;
+	}
+
+	if (g_variant_is_of_type (reply, G_VARIANT_TYPE ("((s(is)bsbb))"))) {
+		g_variant_get (reply, "((&s(i&s)b&sbb))",
+		               &key, &type, &value,
+		               &is_set, &schema,
+		               &is_default, &is_writable,
+		               NULL);
+
+		val = g_strdup (value);
+	}
+
+	g_variant_unref (reply);
+
+	return val;
+}
+
+void
+tracker_locale_gconfdbus_init (void)
+{
+	if (!g_getenv (TRACKER_DISABLE_MEEGOTOUCH_LOCALE_ENV)) {
+		GError *error = NULL;
+		GVariant *reply;
+		guint i;
+
+		GDBusInterfaceVTable interface_vtable = {
+			handle_method_call,
+			handle_get_property,
+			handle_set_property
+		};
+
+		g_message ("Retrieving locale from GConf is ENABLED");
+
+		connection = g_bus_get_sync (G_BUS_TYPE_SESSION, NULL, &error);
+
+		if (error) {
+			g_critical ("%s", error->message);
+			g_clear_error (&error);
+
+			return;
+		}
+
+		service_running = TRUE;
+
+		reply = g_dbus_connection_call_sync (connection,
+		                                     GCONF_DBUS_SERVICE,
+		                                     GCONF_DBUS_SERVER_OBJECT,
+		                                     GCONF_DBUS_SERVER_INTERFACE,
+		                                     "GetDefaultDatabase",
+		                                     NULL,
+		                                     NULL,
+		                                     G_DBUS_CALL_FLAGS_NONE,
+		                                     -1,
+		                                     NULL,
+		                                     &error);
+
+
+		if (error) {
+			g_critical ("%s", error->message);
+			g_clear_error (&error);
+			return;
+		}
+
+		g_variant_get (reply, "(s)", &gconf_dbus_default_db, NULL);
+
+		g_variant_unref (reply);
+
+		introspection_data = g_dbus_node_info_new_for_xml (introspection_xml, &error);
+
+		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;
+		}
+
+		if (add_notify ()) {
+			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);
+		}
+
+		/* And 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);
+			}
+		}
+	}
+}
+
+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
new file mode 100644
index 0000000..130a0c0
--- /dev/null
+++ b/src/libtracker-common/tracker-locale-gconfdbus.h
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2010 Nokia <ivan frade nokia com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA  02110-1301, USA.
+ */
+
+#ifndef __LIBTRACKER_COMMON_LOCALE_GCONFDBUS_H__
+#define __LIBTRACKER_COMMON_LOCALE_GCONFDBUS_H__
+
+#include <glib.h>
+
+G_BEGIN_DECLS
+
+#if !defined (__LIBTRACKER_COMMON_INSIDE__) && !defined (TRACKER_COMPILATION)
+#error "only <libtracker-common/tracker-common.h> must be included directly."
+#endif
+
+#include "tracker-locale.h"
+
+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 3a00cb1..f2e91fa 100644
--- a/src/libtracker-common/tracker-locale.c
+++ b/src/libtracker-common/tracker-locale.c
@@ -27,33 +27,7 @@
 #include "tracker-locale.h"
 
 #ifdef HAVE_MAEMO
-#include <gconf/gconf-client.h>
-#endif /* HAVE_MAEMO */
-
-#ifdef HAVE_MAEMO
-
-/* In src/libtracker-sparql/tracker-init.c you'll find a first call to
- * gconf_client_get_default() in a function that has the GCC specific
- * construction attribute. This is there because GConfClient isn't thread-safe
- * or at least not "first call is in thread vs. mainloop"-safe. The reason
- * why it must be in libtracker-sparql instead of here is because this code
- * is entered using g_module_open (dlopen) by tracker_sparql_backend_load_
- * plugins_from_path: the construction attribute would only be detected at
- * dlopen, which would be in the thread that calls load_plugins_from_path
- * caused by a tracker_sparql_connection_get_async. It's a bit unfortunate
- * and perhaps ugly, but fortunately we only need it in HAVE_MAEMO */
-
-/* Mutex to sync access to the current locale values */
-static GStaticMutex locales_mutex = G_STATIC_MUTEX_INIT;
-static GStaticMutex subscribers_mutex = G_STATIC_MUTEX_INIT;
-#define LOCK_LOCALES g_static_mutex_lock (&locales_mutex)
-#define UNLOCK_LOCALES g_static_mutex_unlock (&locales_mutex)
-#define LOCK_SUBSCRIBERS g_static_mutex_lock (&subscribers_mutex)
-#define UNLOCK_SUBSCRIBERS g_static_mutex_unlock (&subscribers_mutex)
-#else
-/* If not using gconf locales, no need to acquire/release any lock */
-#define LOCK_LOCALES
-#define UNLOCK_LOCALES
+#include "tracker-locale-gconfdbus.h"
 #endif /* HAVE_MAEMO */
 
 /* Current locales in use. They will be stored in heap and available throughout
@@ -61,7 +35,7 @@ static GStaticMutex subscribers_mutex = G_STATIC_MUTEX_INIT;
  */
 static gchar *current_locales[TRACKER_LOCALE_LAST];
 
-static gchar *locale_names[TRACKER_LOCALE_LAST] = {
+static const gchar *locale_names[TRACKER_LOCALE_LAST] = {
 	"TRACKER_LOCALE_LANGUAGE",
 	"TRACKER_LOCALE_TIME",
 	"TRACKER_LOCALE_COLLATE",
@@ -71,49 +45,21 @@ static gchar *locale_names[TRACKER_LOCALE_LAST] = {
 
 /* Already initialized? */
 static gboolean initialized;
+static GStaticRecMutex locales_mutex = G_STATIC_REC_MUTEX_INIT;
 
-#ifdef HAVE_MAEMO
-
-/* If this envvar is set, even if we have meegotouch locales in gconf,
- * we'll still use envvars */
-#define TRACKER_DISABLE_MEEGOTOUCH_LOCALE_ENV "TRACKER_DISABLE_MEEGOTOUCH_LOCALE"
-
-/* Base dir for all gconf locale values */
-#define MEEGOTOUCH_LOCALE_DIR "/meegotouch/i18n"
-
-/* gconf keys for tracker locales, as defined in:
- * http://apidocs.meego.com/1.0/mtf/i18n.html
- */
-static const gchar *gconf_locales[TRACKER_LOCALE_LAST] = {
-	MEEGOTOUCH_LOCALE_DIR "/language",
-	MEEGOTOUCH_LOCALE_DIR "/lc_time",
-	MEEGOTOUCH_LOCALE_DIR "/lc_collate",
-	MEEGOTOUCH_LOCALE_DIR "/lc_numeric",
-	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;
-
-/* The gconf client, which will be created once at initialization and
- * the reference will never be destroyed, so will be reported as still
- * reachable by Valgrind */
-static GConfClient *client;
-
-#endif /* HAVE_MAEMO */
+const gchar*
+tracker_locale_get_name (guint i)
+{
+	g_return_val_if_fail (i < TRACKER_LOCALE_LAST, NULL);
+	return locale_names[i];
+}
 
-static void
-locale_set (TrackerLocaleID  id,
-            const gchar     *value)
+void
+tracker_locale_set (TrackerLocaleID  id,
+                    const gchar     *value)
 {
+	g_static_rec_mutex_lock (&locales_mutex);
+
 	if (current_locales[id]) {
 		g_debug ("Locale '%s' was changed from '%s' to '%s'",
 		         locale_names[id],
@@ -151,87 +97,10 @@ locale_set (TrackerLocaleID  id,
 		g_warn_if_reached ();
 		break;
 	}
-}
-
-#ifdef HAVE_MAEMO
-
-static void
-locale_gconf_notify_cb (GConfClient *client,
-                        guint        cnxn_id,
-                        GConfEntry  *entry,
-                        gpointer     user_data)
-{
-	guint i;
-	GConfValue *value;
-	GSList *li;
-	const gchar *string_value;
-
-	/* Find the proper locale to change */
-	for (i = 0; i < TRACKER_LOCALE_LAST; i++) {
-		if (strcmp (gconf_locales[i], gconf_entry_get_key (entry)) == 0) {
-			break;
-		}
-	}
-
-	/* Oh, not found? */
-	if (i == TRACKER_LOCALE_LAST) {
-		g_debug ("Skipping change on gconf key '%s' as not really needed",
-		         gconf_entry_get_key (entry));
-		return;
-	}
-
-	/* Ensure a proper value was set */
-	value = gconf_entry_get_value (entry);
-	if (!value) {
-		g_warning ("Locale value for '%s' cannot be NULL, not changing %s",
-		           gconf_locales[i],
-		           locale_names[i]);
-		return;
-	}
-
-	/* It must be a string */
-	if (value->type != GCONF_VALUE_STRING) {
-		g_warning ("Locale value for '%s' must be a string, not changing %s",
-		           gconf_locales[i],
-		           locale_names[i]);
-		return;
-	}
-
-	string_value = gconf_value_get_string (value);
-
-	/* String must have a length > 0 */
-	if (!string_value ||
-	    string_value[0] == '\0') {
-		g_warning ("Locale value for '%s' must not be empty, not changing %s",
-		           gconf_locales[i],
-		           locale_names[i]);
-		return;
-	}
-
-	/* Protect the locale change with the lock */
-	LOCK_LOCALES;
-	locale_set (i, gconf_value_get_string (value));
-	UNLOCK_LOCALES;
 
-	/* Now, if any subscriber, notify the locale change.
-	 * NOTE!!!! The callback MUST NOT perform any action
-	 * that may change the list of subscribers, or the
-	 * program will get locked. */
-	LOCK_SUBSCRIBERS;
-	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'",
-			         locale_names[i],
-			         data);
-			data->func (i, data->user_data);
-		}
-	}
-	UNLOCK_SUBSCRIBERS;
+	g_static_rec_mutex_unlock (&locales_mutex);
 }
 
-#endif /* HAVE_MAEMO */
 
 static void
 locale_init (void)
@@ -239,71 +108,7 @@ locale_init (void)
 	guint i;
 
 #ifdef HAVE_MAEMO
-	if (g_getenv (TRACKER_DISABLE_MEEGOTOUCH_LOCALE_ENV)) {
-		g_message ("Retrieving locale from GConf is DISABLED");
-	} else {
-		GError *error = NULL;
-
-		g_message ("Retrieving locale from GConf is ENABLED");
-
-		/* Get default gconf client to query the locale values */
-		client = gconf_client_get_default ();
-
-		/* We want to be notified when locales are changed in gconf */
-		gconf_client_add_dir (client,
-		                      MEEGOTOUCH_LOCALE_DIR,
-		                      GCONF_CLIENT_PRELOAD_ONELEVEL,
-		                      &error);
-		if (error) {
-			g_warning ("Cannot add dir '%s' in gconf client: '%s'",
-			           MEEGOTOUCH_LOCALE_DIR,
-			           error->message);
-			g_clear_error (&error);
-		} else {
-			/* Request notifications */
-			gconf_client_notify_add (client,
-			                         MEEGOTOUCH_LOCALE_DIR,
-			                         locale_gconf_notify_cb,
-			                         NULL,
-			                         NULL,
-			                         &error);
-			if (error) {
-				g_warning ("Cannot request notifications under dir '%s' in "
-				           "gconf client: '%s'",
-				           MEEGOTOUCH_LOCALE_DIR,
-				           error->message);
-				g_clear_error (&error);
-			}
-		}
-
-		/* And initialize all, should have been all preloaded in the
-		 * client already */
-		for (i = 0; i < TRACKER_LOCALE_LAST; i++) {
-			GConfValue *val;
-
-			/* Get the gconf key, if any */
-			val = gconf_client_get (client,
-			                        gconf_locales[i],
-			                        &error);
-			if (!val) {
-				g_warning ("Couldn't get value for key '%s'"
-				           " from gconf: '%s'"
-				           " Defaulting to environment locale.",
-				           gconf_locales[i],
-				           error ? error->message : "no such key");
-				g_clear_error (&error);
-			} else if (val->type != GCONF_VALUE_STRING) {
-				g_warning ("Wrong type for '%s' key in gconf..."
-				           " Defaulting to environment locale.",
-				           gconf_locales[i]);
-				gconf_value_free (val);
-			} else {
-				/* Set the new locale */
-				locale_set (i, gconf_value_get_string (val));
-				gconf_value_free (val);
-			}
-		}
-	}
+	tracker_locale_gconfdbus_init ();
 #endif /* HAVE_MAEMO */
 
 	/* Initialize those not retrieved from gconf, or if not in maemo */
@@ -335,9 +140,9 @@ locale_init (void)
 
 			if (!env_locale) {
 				g_warning ("Locale '%d' is not set, defaulting to C locale", i);
-				locale_set (i, "C");
+				tracker_locale_set (i, "C");
 			} else {
-				locale_set (i, env_locale);
+				tracker_locale_set (i, env_locale);
 			}
 		}
 	}
@@ -351,19 +156,18 @@ tracker_locale_get (TrackerLocaleID id)
 {
 	gchar *locale;
 
-	/* Lock even before checking if initialized, so that initialization is
-	 * not done twice */
-	LOCK_LOCALES;
+	g_static_rec_mutex_lock (&locales_mutex);
 
 	/* Initialize if not already done */
-	if (!initialized)
+	if (!initialized) {
 		locale_init ();
+	}
 
 	/* Always return a duplicated string, as the locale may change at any
 	 * moment */
 	locale = g_strdup (current_locales[id]);
 
-	UNLOCK_LOCALES;
+	g_static_rec_mutex_unlock (&locales_mutex);
 
 	return locale;
 }
@@ -375,22 +179,7 @@ tracker_locale_notify_add (TrackerLocaleID         id,
                            GFreeFunc               destroy_notify)
 {
 #ifdef HAVE_MAEMO
-	TrackerLocaleNotification *data;
-
-	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;
-
-	/* Lock list, we cannot! use the same lock as for locales here... */
-	LOCK_SUBSCRIBERS;
-	subscribers = g_slist_prepend (subscribers, data);
-	UNLOCK_SUBSCRIBERS;
-
-	return data;
+	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;
@@ -401,23 +190,7 @@ void
 tracker_locale_notify_remove (gpointer notification_id)
 {
 #ifdef HAVE_MAEMO
-	GSList *li;
-
-	LOCK_SUBSCRIBERS;
-	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);
-
-		/* 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);
-	}
-	UNLOCK_SUBSCRIBERS;
+	return tracker_locale_gconfdbus_notify_remove (notification_id);
 #else
 	/* If not using gconf locales, this is a no-op... */
 #endif /* HAVE_MAEMO */
diff --git a/src/libtracker-common/tracker-locale.h b/src/libtracker-common/tracker-locale.h
index 49dac0b..a7c2934 100644
--- a/src/libtracker-common/tracker-locale.h
+++ b/src/libtracker-common/tracker-locale.h
@@ -39,25 +39,28 @@ typedef enum {
 } TrackerLocaleID;
 
 /* Callback for the notification of locale changes */
-typedef void (* TrackerLocaleNotifyFunc) (TrackerLocaleID id,
-                                          gpointer user_data);
+typedef void (* TrackerLocaleNotifyFunc)  (TrackerLocaleID id,
+                                           gpointer user_data);
 
 /* Get the current locale of the given type.
  * Note that it returns a newly-allocated string which should be g_free()-ed
  */
-gchar    *tracker_locale_get           (TrackerLocaleID id);
+gchar       *tracker_locale_get           (TrackerLocaleID id);
 
 /* Adds a new subscriber to locale change notifications.
  * Returns a pointer which identifies the subscription.
  */
-gpointer  tracker_locale_notify_add    (TrackerLocaleID         id,
-                                        TrackerLocaleNotifyFunc func,
-                                        gpointer                user_data,
-                                        GFreeFunc               destroy_notify);
+gpointer     tracker_locale_notify_add    (TrackerLocaleID         id,
+                                           TrackerLocaleNotifyFunc func,
+                                           gpointer                user_data,
+                                           GFreeFunc               destroy_notify);
 
 /* Remove a given subscriber, passing the id you got in _add() */
-void      tracker_locale_notify_remove (gpointer notification_id);
+void         tracker_locale_notify_remove (gpointer                notification_id);
 
+const gchar* tracker_locale_get_name      (guint                   i);
+void         tracker_locale_set           (TrackerLocaleID         id,
+                                           const gchar            *value);
 
 G_END_DECLS
 



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