diff options
author | Milan Crha <mcrha@redhat.com> | 2018-03-12 14:06:44 +0100 |
---|---|---|
committer | Milan Crha <mcrha@redhat.com> | 2018-03-12 14:06:44 +0100 |
commit | 7ca206e0c0044bb6f85b8421562f2e42002f6283 (patch) | |
tree | da32f37416626cfed6e95941354b3eea1a73b783 /src/addressbook/libedata-book | |
parent | d55d0735657d02b156da7664ed40b5f36cb3b5af (diff) | |
download | evolution-data-server-7ca206e0c0044bb6f85b8421562f2e42002f6283.tar.gz |
Bug 793031 - Decrease memory usage by disabling backend-per-process by default ][
A follow-up change, with more aggressive fix, which doesn't start
factory subprocesses but runs backends in the main factory process.
Diffstat (limited to 'src/addressbook/libedata-book')
-rw-r--r-- | src/addressbook/libedata-book/CMakeLists.txt | 2 | ||||
-rw-r--r-- | src/addressbook/libedata-book/e-data-book-factory.c | 179 | ||||
-rw-r--r-- | src/addressbook/libedata-book/e-subprocess-book-factory.c | 272 | ||||
-rw-r--r-- | src/addressbook/libedata-book/e-system-locale-watcher.c | 365 | ||||
-rw-r--r-- | src/addressbook/libedata-book/e-system-locale-watcher.h | 75 | ||||
-rw-r--r-- | src/addressbook/libedata-book/libedata-book.h | 1 |
6 files changed, 660 insertions, 234 deletions
diff --git a/src/addressbook/libedata-book/CMakeLists.txt b/src/addressbook/libedata-book/CMakeLists.txt index 2bac3a979..994d5f40a 100644 --- a/src/addressbook/libedata-book/CMakeLists.txt +++ b/src/addressbook/libedata-book/CMakeLists.txt @@ -27,6 +27,7 @@ set(SOURCES e-data-book-factory.c e-data-book-view.c e-subprocess-book-factory.c + e-system-locale-watcher.c ximian-vcard.h ) @@ -49,6 +50,7 @@ set(HEADERS e-book-backend-cache.h e-book-backend-sqlitedb.h e-subprocess-book-factory.h + e-system-locale-watcher.h ) if(WITH_LIBDB) diff --git a/src/addressbook/libedata-book/e-data-book-factory.c b/src/addressbook/libedata-book/e-data-book-factory.c index d34b65f0d..5527d58d8 100644 --- a/src/addressbook/libedata-book/e-data-book-factory.c +++ b/src/addressbook/libedata-book/e-data-book-factory.c @@ -30,10 +30,6 @@ **/ #include "evolution-data-server-config.h" -#include <locale.h> -#include <stdlib.h> -#include <string.h> -#include <unistd.h> #include <glib/gi18n.h> /* Private D-Bus classes. */ @@ -43,6 +39,7 @@ #include "e-book-backend-factory.h" #include "e-data-book.h" #include "e-data-book-factory.h" +#include "e-system-locale-watcher.h" #define d(x) @@ -52,6 +49,9 @@ struct _EDataBookFactoryPrivate { EDBusAddressBookFactory *dbus_factory; + + ESystemLocaleWatcher *locale_watcher; + gulong notify_locale_id; }; /* Forward Declarations */ @@ -117,15 +117,177 @@ data_book_factory_handle_open_address_book_cb (EDBusAddressBookFactory *iface, } static void +data_book_factory_backend_closed_cb (EBackend *backend, + const gchar *sender, + EDataFactory *data_factory) +{ + e_data_factory_backend_closed (data_factory, backend); +} + +static EBackend * +data_book_factory_create_backend (EDataFactory *data_factory, + EBackendFactory *backend_factory, + ESource *source) +{ + EBookBackendFactoryClass *backend_factory_class; + EBackend *backend; + + g_return_val_if_fail (E_IS_DATA_BOOK_FACTORY (data_factory), NULL); + g_return_val_if_fail (E_IS_BOOK_BACKEND_FACTORY (backend_factory), NULL); + g_return_val_if_fail (E_IS_SOURCE (source), NULL); + + backend_factory_class = E_BOOK_BACKEND_FACTORY_GET_CLASS (backend_factory); + g_return_val_if_fail (backend_factory_class != NULL, NULL); + + if (g_type_is_a (backend_factory_class->backend_type, G_TYPE_INITABLE)) { + GError *local_error = NULL; + + backend = g_initable_new (backend_factory_class->backend_type, NULL, &local_error, + "registry", e_data_factory_get_registry (data_factory), + "source", source, + NULL); + + if (!backend) + g_warning ("%s: Failed to create backend: %s\n", G_STRFUNC, local_error ? local_error->message : "Unknown error"); + + g_clear_error (&local_error); + } else { + backend = g_object_new (backend_factory_class->backend_type, + "registry", e_data_factory_get_registry (data_factory), + "source", source, + NULL); + } + + if (backend) { + g_signal_connect (backend, "closed", + G_CALLBACK (data_book_factory_backend_closed_cb), data_factory); + } + + return backend; +} + +static gchar * +data_book_factory_open_backend (EDataFactory *data_factory, + EBackend *backend, + GDBusConnection *connection, + GCancellable *cancellable, + GError **error) +{ + EDataBook *data_book; + gchar *object_path; + + g_return_val_if_fail (E_IS_DATA_BOOK_FACTORY (data_factory), NULL); + g_return_val_if_fail (E_IS_BOOK_BACKEND (backend), NULL); + g_return_val_if_fail (G_IS_DBUS_CONNECTION (connection), NULL); + + /* If the backend already has an EDataBook installed, return its + * object path. Otherwise we need to install a new EDataBook. */ + data_book = e_book_backend_ref_data_book (E_BOOK_BACKEND (backend)); + + if (data_book != NULL) { + object_path = g_strdup (e_data_book_get_object_path (data_book)); + } else { + object_path = e_subprocess_factory_construct_path (); + + /* The EDataBook will attach itself to EBookBackend, + * so no need to call e_book_backend_set_data_book(). */ + data_book = e_data_book_new (E_BOOK_BACKEND (backend), connection, object_path, error); + + if (data_book) { + EDataBookFactory *data_book_factory = E_DATA_BOOK_FACTORY (data_factory); + gchar *locale; + + locale = e_system_locale_watcher_dup_locale (data_book_factory->priv->locale_watcher); + + /* Don't set the locale on a new book if we have not + * yet received a notification of a locale change + */ + if (locale) + e_data_book_set_locale (data_book, locale, NULL, NULL); + + g_free (locale); + } else { + g_free (object_path); + object_path = NULL; + } + } + + g_clear_object (&data_book); + + return object_path; +} + +static void +data_book_factory_notify_locale_cb (GObject *object, + GParamSpec *pspec, + gpointer user_data) +{ + ESystemLocaleWatcher *watcher = E_SYSTEM_LOCALE_WATCHER (object); + EDataBookFactory *data_book_factory = E_DATA_BOOK_FACTORY (user_data); + gchar *locale; + + locale = e_system_locale_watcher_dup_locale (watcher); + + if (locale) { + GSList *backends, *link; + GError *local_error = NULL; + + backends = e_data_factory_list_opened_backends (E_DATA_FACTORY (data_book_factory)); + + for (link = backends; link; link = g_slist_next (link)) { + EBackend *backend = link->data; + EDataBook *data_book; + + data_book = e_book_backend_ref_data_book (E_BOOK_BACKEND (backend)); + + if (!e_data_book_set_locale (data_book, locale, NULL, &local_error)) { + g_warning ("Failed to set locale on addressbook: %s", local_error ? local_error->message : "Unknown error"); + g_clear_error (&local_error); + } + + g_object_unref (data_book); + } + + g_slist_free_full (backends, g_object_unref); + g_free (locale); + } +} + +static void +data_book_factory_constructed (GObject *object) +{ + EDataBookFactory *data_book_factory; + + /* Chain up to parent's method. */ + G_OBJECT_CLASS (e_data_book_factory_parent_class)->constructed (object); + + data_book_factory = E_DATA_BOOK_FACTORY (object); + + /* Listen to locale changes only when the backends run in the factory process, + aka when the backend-per-process is disabled */ + if (!e_data_factory_use_backend_per_process (E_DATA_FACTORY (data_book_factory))) { + data_book_factory->priv->locale_watcher = e_system_locale_watcher_new (); + + data_book_factory->priv->notify_locale_id = g_signal_connect ( + data_book_factory->priv->locale_watcher, "notify::locale", + G_CALLBACK (data_book_factory_notify_locale_cb), data_book_factory); + } +} + +static void data_book_factory_dispose (GObject *object) { EDataBookFactory *factory; - EDataBookFactoryPrivate *priv; factory = E_DATA_BOOK_FACTORY (object); - priv = factory->priv; - g_clear_object (&priv->dbus_factory); + if (factory->priv->locale_watcher && factory->priv->notify_locale_id) { + g_signal_handler_disconnect (factory->priv->locale_watcher, factory->priv->notify_locale_id); + factory->priv->notify_locale_id = 0; + } + + g_clear_object (&factory->priv->dbus_factory); + g_clear_object (&factory->priv->locale_watcher); /* Chain up to parent's dispose() method. */ G_OBJECT_CLASS (e_data_book_factory_parent_class)->dispose (object); @@ -154,6 +316,7 @@ e_data_book_factory_class_init (EDataBookFactoryClass *class) g_type_class_add_private (class, sizeof (EDataBookFactoryPrivate)); object_class = G_OBJECT_CLASS (class); + object_class->constructed = data_book_factory_constructed; object_class->dispose = data_book_factory_dispose; dbus_server_class = E_DBUS_SERVER_CLASS (class); @@ -168,6 +331,8 @@ e_data_book_factory_class_init (EDataBookFactoryClass *class) data_factory_class->get_dbus_interface_skeleton = data_book_factory_get_dbus_interface_skeleton; data_factory_class->get_factory_name = data_book_get_factory_name; data_factory_class->complete_open = data_book_complete_open; + data_factory_class->create_backend = data_book_factory_create_backend; + data_factory_class->open_backend = data_book_factory_open_backend; } static void diff --git a/src/addressbook/libedata-book/e-subprocess-book-factory.c b/src/addressbook/libedata-book/e-subprocess-book-factory.c index 406285fb5..80b5ffedf 100644 --- a/src/addressbook/libedata-book/e-subprocess-book-factory.c +++ b/src/addressbook/libedata-book/e-subprocess-book-factory.c @@ -25,16 +25,12 @@ #include "evolution-data-server-config.h" -#include <locale.h> -#include <stdlib.h> -#include <string.h> -#include <unistd.h> #include <glib/gi18n-lib.h> #include "e-book-backend.h" #include "e-book-backend-factory.h" #include "e-data-book.h" -#include "e-dbus-localed.h" +#include "e-system-locale-watcher.h" #include "e-subprocess-book-factory.h" #include <e-dbus-subprocess-backend.h> @@ -44,16 +40,10 @@ ((obj), E_TYPE_SUBPROCESS_BOOK_FACTORY, ESubprocessBookFactoryPrivate)) struct _ESubprocessBookFactoryPrivate { - /* Watching "org.freedesktop.locale1" for locale changes */ - guint localed_watch_id; - guint subprocess_watch_id; - EDBusLocale1 *localed_proxy; - GCancellable *localed_cancel; - gchar *locale; + ESystemLocaleWatcher *locale_watcher; + gulong notify_locale_id; }; -static GInitableIface *initable_parent_interface; - /* Forward Declarations */ static void e_subprocess_book_factory_initable_init (GInitableIface *iface); @@ -94,17 +84,19 @@ subprocess_book_factory_open (ESubprocessFactory *subprocess_factory, connection, object_path, error); if (data_book != NULL) { - e_subprocess_factory_set_backend_callbacks ( - subprocess_factory, backend, data); + gchar *locale; + + e_subprocess_factory_set_backend_callbacks (subprocess_factory, backend, data); + + locale = e_system_locale_watcher_dup_locale (subprocess_book_factory->priv->locale_watcher); /* Don't set the locale on a new book if we have not * yet received a notification of a locale change */ - if (subprocess_book_factory->priv->locale) - e_data_book_set_locale ( - data_book, - subprocess_book_factory->priv->locale, - NULL, NULL); + if (locale) + e_data_book_set_locale (data_book, locale, NULL, NULL); + + g_free (locale); } else { g_free (object_path); object_path = NULL; @@ -139,211 +131,71 @@ subprocess_book_factory_ref_backend (ESourceRegistry *registry, } static void -subprocess_book_factory_dispose (GObject *object) -{ - ESubprocessBookFactory *subprocess_factory; - ESubprocessBookFactoryPrivate *priv; - - subprocess_factory = E_SUBPROCESS_BOOK_FACTORY (object); - priv = subprocess_factory->priv; - - if (priv->localed_cancel) - g_cancellable_cancel (priv->localed_cancel); - - g_clear_object (&priv->localed_cancel); - g_clear_object (&priv->localed_proxy); - - if (priv->localed_watch_id > 0) - g_bus_unwatch_name (priv->localed_watch_id); - - if (priv->subprocess_watch_id > 0) - g_bus_unwatch_name (priv->subprocess_watch_id); - - /* Chain up to parent's dispose() method. */ - G_OBJECT_CLASS (e_subprocess_book_factory_parent_class)->dispose (object); -} - -static void -subprocess_book_factory_finalize (GObject *object) -{ - ESubprocessBookFactory *subprocess_factory; - - subprocess_factory = E_SUBPROCESS_BOOK_FACTORY (object); - - g_free (subprocess_factory->priv->locale); - - /* Chain up to parent's finalize() method. */ - G_OBJECT_CLASS (e_subprocess_book_factory_parent_class)->finalize (object); -} - -static gchar * -subprocess_book_factory_interpret_locale_value (const gchar *value) +subprocess_book_factory_notify_locale_cb (GObject *object, + GParamSpec *pspec, + gpointer user_data) { - gchar *interpreted_value = NULL; - gchar **split; - - split = g_strsplit (value, "=", 2); - - if (split && split[0] && split[1]) - interpreted_value = g_strdup (split[1]); - - g_strfreev (split); + ESystemLocaleWatcher *watcher = E_SYSTEM_LOCALE_WATCHER (object); + ESubprocessBookFactory *subprocess_factory = E_SUBPROCESS_BOOK_FACTORY (user_data); + gchar *locale; - if (!interpreted_value) - g_warning ("Failed to interpret locale value: %s", value); + locale = e_system_locale_watcher_dup_locale (watcher); - return interpreted_value; -} - -static gchar * -subprocess_book_factory_interpret_locale (const gchar * const * locale) -{ - gint i; - gchar *interpreted_locale = NULL; - - /* Prioritize LC_COLLATE and then LANG values - * in the 'locale' specified by localed. - * - * If localed explicitly specifies no locale, then - * default to checking system locale. - */ if (locale) { - for (i = 0; locale[i] != NULL && interpreted_locale == NULL; i++) { - if (strncmp (locale[i], "LC_COLLATE", 10) == 0) - interpreted_locale = - subprocess_book_factory_interpret_locale_value (locale[i]); - } - - for (i = 0; locale[i] != NULL && interpreted_locale == NULL; i++) { - if (strncmp (locale[i], "LANG", 4) == 0) - interpreted_locale = - subprocess_book_factory_interpret_locale_value (locale[i]); - } - } - - if (!interpreted_locale) { - const gchar *system_locale = setlocale (LC_COLLATE, NULL); - - interpreted_locale = g_strdup (system_locale); - } - - return interpreted_locale; -} - -static void -subprocess_book_factory_set_locale (ESubprocessBookFactory *subprocess_factory, - const gchar *locale) -{ - ESubprocessBookFactoryPrivate *priv = subprocess_factory->priv; - GError *error = NULL; - - if (g_strcmp0 (priv->locale, locale) != 0) { - GList *backends, *l; - - g_free (priv->locale); - priv->locale = g_strdup (locale); + GList *backends, *link; + GError *local_error = NULL; backends = e_subprocess_factory_get_backends_list (E_SUBPROCESS_FACTORY (subprocess_factory)); - for (l = backends; l != NULL; l = g_list_next (l)) { - EBackend *backend = l->data; + for (link = backends; link; link = g_list_next (link)) { + EBackend *backend = link->data; EDataBook *data_book; data_book = e_book_backend_ref_data_book (E_BOOK_BACKEND (backend)); - if (!e_data_book_set_locale (data_book, locale, NULL, &error)) { - g_warning ( - "Failed to set locale on addressbook: %s", - error->message); - g_clear_error (&error); + if (!e_data_book_set_locale (data_book, locale, NULL, &local_error)) { + g_warning ("Failed to set locale on addressbook: %s", local_error ? local_error->message : "Unknown error"); + g_clear_error (&local_error); } g_object_unref (data_book); } g_list_free_full (backends, g_object_unref); + g_free (locale); } } static void -subprocess_book_factory_locale_changed (GObject *object, - GParamSpec *pspec, - gpointer user_data) +subprocess_book_factory_constructed (GObject *object) { - EDBusLocale1 *locale_proxy = E_DBUS_LOCALE1 (object); - ESubprocessBookFactory *factory = (ESubprocessBookFactory *) user_data; - const gchar * const *locale; - gchar *interpreted_locale; + ESubprocessBookFactory *subprocess_factory; - locale = e_dbus_locale1_get_locale (locale_proxy); - interpreted_locale = subprocess_book_factory_interpret_locale (locale); + /* Chain up to parent's method */ + G_OBJECT_CLASS (e_subprocess_book_factory_parent_class)->constructed (object); - subprocess_book_factory_set_locale (factory, interpreted_locale); + subprocess_factory = E_SUBPROCESS_BOOK_FACTORY (object); + subprocess_factory->priv->locale_watcher = e_system_locale_watcher_new (); - g_free (interpreted_locale); + subprocess_factory->priv->notify_locale_id = g_signal_connect ( + subprocess_factory->priv->locale_watcher, "notify::locale", + G_CALLBACK (subprocess_book_factory_notify_locale_cb), subprocess_factory); } static void -subprocess_book_factory_localed_ready (GObject *source_object, - GAsyncResult *res, - gpointer user_data) +subprocess_book_factory_dispose (GObject *object) { - ESubprocessBookFactory *subprocess_factory = (ESubprocessBookFactory *) user_data; - GError *error = NULL; - - subprocess_factory->priv->localed_proxy = e_dbus_locale1_proxy_new_finish (res, &error); + ESubprocessBookFactory *subprocess_factory = E_SUBPROCESS_BOOK_FACTORY (object); - if (subprocess_factory->priv->localed_proxy == NULL) { - g_warning ("Error fetching localed proxy: %s", error->message); - g_error_free (error); + if (subprocess_factory->priv->locale_watcher && subprocess_factory->priv->notify_locale_id) { + g_signal_handler_disconnect (subprocess_factory->priv->locale_watcher, subprocess_factory->priv->notify_locale_id); + subprocess_factory->priv->notify_locale_id = 0; } - g_clear_object (&subprocess_factory->priv->localed_cancel); - - if (subprocess_factory->priv->localed_proxy) { - g_signal_connect ( - subprocess_factory->priv->localed_proxy, "notify::locale", - G_CALLBACK (subprocess_book_factory_locale_changed), subprocess_factory); + g_clear_object (&subprocess_factory->priv->locale_watcher); - /* Initial refresh of the locale */ - subprocess_book_factory_locale_changed ( - G_OBJECT (subprocess_factory->priv->localed_proxy), NULL, subprocess_factory); - } -} - -static void -subprocess_book_factory_localed_appeared (GDBusConnection *connection, - const gchar *name, - const gchar *name_owner, - gpointer user_data) -{ - ESubprocessBookFactory *subprocess_factory = (ESubprocessBookFactory *) user_data; - - subprocess_factory->priv->localed_cancel = g_cancellable_new (); - - e_dbus_locale1_proxy_new ( - connection, - G_DBUS_PROXY_FLAGS_GET_INVALIDATED_PROPERTIES, - "org.freedesktop.locale1", - "/org/freedesktop/locale1", - subprocess_factory->priv->localed_cancel, - subprocess_book_factory_localed_ready, - subprocess_factory); -} - -static void -subprocess_book_factory_localed_vanished (GDBusConnection *connection, - const gchar *name, - gpointer user_data) -{ - ESubprocessBookFactory *subprocess_factory = (ESubprocessBookFactory *) user_data; - - if (subprocess_factory->priv->localed_cancel) { - g_cancellable_cancel (subprocess_factory->priv->localed_cancel); - g_clear_object (&subprocess_factory->priv->localed_cancel); - } - - g_clear_object (&subprocess_factory->priv->localed_proxy); + /* Chain up to parent's dispose() method. */ + G_OBJECT_CLASS (e_subprocess_book_factory_parent_class)->dispose (object); } static void @@ -355,51 +207,17 @@ e_subprocess_book_factory_class_init (ESubprocessBookFactoryClass *class) g_type_class_add_private (class, sizeof (ESubprocessBookFactoryPrivate)); object_class = G_OBJECT_CLASS (class); + object_class->constructed = subprocess_book_factory_constructed; object_class->dispose = subprocess_book_factory_dispose; - object_class->finalize = subprocess_book_factory_finalize; subprocess_factory_class = E_SUBPROCESS_FACTORY_CLASS (class); subprocess_factory_class->ref_backend = subprocess_book_factory_ref_backend; subprocess_factory_class->open_data = subprocess_book_factory_open; } -static gboolean -subprocess_book_factory_initable_init (GInitable *initable, - GCancellable *cancellable, - GError **error) -{ - ESubprocessBookFactory *subprocess_factory; - GBusType bus_type = G_BUS_TYPE_SYSTEM; - - subprocess_factory = E_SUBPROCESS_BOOK_FACTORY (initable); - - /* When running tests, we pretend to be the "org.freedesktop.locale1" service - * on the session bus instead of the real location on the system bus. - */ - if (g_getenv ("EDS_TESTING") != NULL) - bus_type = G_BUS_TYPE_SESSION; - - /* Watch system bus for locale change notifications */ - subprocess_factory->priv->localed_watch_id = - g_bus_watch_name ( - bus_type, - "org.freedesktop.locale1", - G_BUS_NAME_WATCHER_FLAGS_NONE, - subprocess_book_factory_localed_appeared, - subprocess_book_factory_localed_vanished, - initable, - NULL); - - /* Chain up to parent interface's init() method. */ - return initable_parent_interface->init (initable, cancellable, error); -} - static void e_subprocess_book_factory_initable_init (GInitableIface *iface) { - initable_parent_interface = g_type_interface_peek_parent (iface); - - iface->init = subprocess_book_factory_initable_init; } static void diff --git a/src/addressbook/libedata-book/e-system-locale-watcher.c b/src/addressbook/libedata-book/e-system-locale-watcher.c new file mode 100644 index 000000000..c8f9b1fa8 --- /dev/null +++ b/src/addressbook/libedata-book/e-system-locale-watcher.c @@ -0,0 +1,365 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ +/* + * Copyright (C) 2018 Red Hat, Inc. (www.redhat.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. + * + * 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, see <http://www.gnu.org/licenses/>. + */ + +/** + * SECTION: e-source-locale-watcher + * @include: libedata-book/libedata-book.h + * @short_description: Watch changes of system locale + * + * #ESystemLocaleWatcher watches for changes of system locale. + **/ + +#include "evolution-data-server-config.h" + +#include <locale.h> +#include <string.h> + +#include "e-dbus-localed.h" + +#include "e-system-locale-watcher.h" + +struct _ESystemLocaleWatcherPrivate { + GMutex lock; + + /* Watching "org.freedesktop.locale1" for locale changes when not using backend-per-process */ + guint localed_watch_id; + EDBusLocale1 *localed_proxy; + GCancellable *localed_cancel; + gchar *locale; +}; + +G_DEFINE_TYPE (ESystemLocaleWatcher, e_system_locale_watcher, G_TYPE_OBJECT) + +enum { + PROP_0, + PROP_LOCALE +}; + +static gchar * +system_locale_watcher_interpret_locale_value (const gchar *value) +{ + gchar *interpreted_value = NULL; + gchar **split; + + split = g_strsplit (value, "=", 2); + + if (split && split[0] && split[1]) + interpreted_value = g_strdup (split[1]); + + g_strfreev (split); + + if (!interpreted_value) + g_warning ("Failed to interpret locale value: %s", value); + + return interpreted_value; +} + +static gchar * +system_locale_watcher_interpret_locale (const gchar * const * locale) +{ + gint i; + gchar *interpreted_locale = NULL; + + /* Prioritize LC_COLLATE and then LANG values + * in the 'locale' specified by localed. + * + * If localed explicitly specifies no locale, then + * default to checking system locale. + */ + if (locale) { + for (i = 0; locale[i] != NULL && interpreted_locale == NULL; i++) { + if (strncmp (locale[i], "LC_COLLATE", 10) == 0) + interpreted_locale = system_locale_watcher_interpret_locale_value (locale[i]); + } + + for (i = 0; locale[i] != NULL && interpreted_locale == NULL; i++) { + if (strncmp (locale[i], "LANG", 4) == 0) + interpreted_locale = system_locale_watcher_interpret_locale_value (locale[i]); + } + } + + if (!interpreted_locale) { + const gchar *system_locale = setlocale (LC_COLLATE, NULL); + + interpreted_locale = g_strdup (system_locale); + } + + return interpreted_locale; +} + +static void +system_locale_watcher_set_locale (ESystemLocaleWatcher *watcher, + const gchar *locale) +{ + g_mutex_lock (&watcher->priv->lock); + + if (g_strcmp0 (watcher->priv->locale, locale) != 0) { + + g_free (watcher->priv->locale); + watcher->priv->locale = g_strdup (locale); + + g_mutex_unlock (&watcher->priv->lock); + + g_object_notify (G_OBJECT (watcher), "locale"); + } else { + g_mutex_unlock (&watcher->priv->lock); + } +} + +static void +system_locale_watcher_locale_changed (GObject *object, + GParamSpec *pspec, + gpointer user_data) +{ + EDBusLocale1 *locale_proxy = E_DBUS_LOCALE1 (object); + ESystemLocaleWatcher *watcher = (ESystemLocaleWatcher *) user_data; + const gchar * const *locale; + gchar *interpreted_locale; + + locale = e_dbus_locale1_get_locale (locale_proxy); + interpreted_locale = system_locale_watcher_interpret_locale (locale); + + system_locale_watcher_set_locale (watcher, interpreted_locale); + + g_free (interpreted_locale); +} + +static void +system_locale_watcher_localed_ready (GObject *source_object, + GAsyncResult *res, + gpointer user_data) +{ + ESystemLocaleWatcher *watcher = (ESystemLocaleWatcher *) user_data; + GError *error = NULL; + + watcher->priv->localed_proxy = e_dbus_locale1_proxy_new_finish (res, &error); + + if (!watcher->priv->localed_proxy) { + g_warning ("Error fetching localed proxy: %s", error ? error->message : "Unknown error"); + g_clear_error (&error); + } + + g_clear_object (&watcher->priv->localed_cancel); + + if (watcher->priv->localed_proxy) { + g_signal_connect ( + watcher->priv->localed_proxy, "notify::locale", + G_CALLBACK (system_locale_watcher_locale_changed), watcher); + + /* Initial refresh of the locale */ + system_locale_watcher_locale_changed (G_OBJECT (watcher->priv->localed_proxy), NULL, watcher); + } +} + +static void +system_locale_watcher_localed_appeared (GDBusConnection *connection, + const gchar *name, + const gchar *name_owner, + gpointer user_data) +{ + ESystemLocaleWatcher *watcher = (ESystemLocaleWatcher *) user_data; + + watcher->priv->localed_cancel = g_cancellable_new (); + + e_dbus_locale1_proxy_new ( + connection, + G_DBUS_PROXY_FLAGS_GET_INVALIDATED_PROPERTIES, + "org.freedesktop.locale1", + "/org/freedesktop/locale1", + watcher->priv->localed_cancel, + system_locale_watcher_localed_ready, + watcher); +} + +static void +system_locale_watcher_localed_vanished (GDBusConnection *connection, + const gchar *name, + gpointer user_data) +{ + ESystemLocaleWatcher *watcher = (ESystemLocaleWatcher *) user_data; + + if (watcher->priv->localed_cancel) { + g_cancellable_cancel (watcher->priv->localed_cancel); + g_clear_object (&watcher->priv->localed_cancel); + } + + g_clear_object (&watcher->priv->localed_proxy); +} + +static void +system_locale_watcher_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec) +{ + switch (property_id) { + case PROP_LOCALE: + g_value_take_string ( + value, + e_system_locale_watcher_dup_locale ( + E_SYSTEM_LOCALE_WATCHER (object))); + return; + } + + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); +} + +static void +system_locale_watcher_constructed (GObject *object) +{ + ESystemLocaleWatcher *watcher = E_SYSTEM_LOCALE_WATCHER (object); + GBusType bus_type = G_BUS_TYPE_SYSTEM; + + /* Chain up to parent's method. */ + G_OBJECT_CLASS (e_system_locale_watcher_parent_class)->constructed (object); + + /* When running tests, we pretend to be the "org.freedesktop.locale1" service + * on the session bus instead of the real location on the system bus. + */ + if (g_getenv ("EDS_TESTING") != NULL) + bus_type = G_BUS_TYPE_SESSION; + + /* Watch system bus for locale change notifications */ + watcher->priv->localed_watch_id = + g_bus_watch_name ( + bus_type, + "org.freedesktop.locale1", + G_BUS_NAME_WATCHER_FLAGS_NONE, + system_locale_watcher_localed_appeared, + system_locale_watcher_localed_vanished, + watcher, + NULL); +} + +static void +system_locale_watcher_dispose (GObject *object) +{ + ESystemLocaleWatcher *watcher = E_SYSTEM_LOCALE_WATCHER (object); + + if (watcher->priv->localed_cancel) + g_cancellable_cancel (watcher->priv->localed_cancel); + + g_clear_object (&watcher->priv->localed_cancel); + g_clear_object (&watcher->priv->localed_proxy); + + if (watcher->priv->localed_watch_id > 0) + g_bus_unwatch_name (watcher->priv->localed_watch_id); + + /* Chain up to parent's method. */ + G_OBJECT_CLASS (e_system_locale_watcher_parent_class)->dispose (object); +} + +static void +system_locale_watcher_finalize (GObject *object) +{ + ESystemLocaleWatcher *watcher = E_SYSTEM_LOCALE_WATCHER (object); + + g_free (watcher->priv->locale); + g_mutex_clear (&watcher->priv->lock); + + /* Chain up to parent's method. */ + G_OBJECT_CLASS (e_system_locale_watcher_parent_class)->finalize (object); +} + +static void +e_system_locale_watcher_class_init (ESystemLocaleWatcherClass *klass) +{ + GObjectClass *object_class; + + g_type_class_add_private (klass, sizeof (ESystemLocaleWatcherPrivate)); + + object_class = G_OBJECT_CLASS (klass); + object_class->get_property = system_locale_watcher_get_property; + object_class->constructed = system_locale_watcher_constructed; + object_class->dispose = system_locale_watcher_dispose; + object_class->finalize = system_locale_watcher_finalize; + + /** + * ESystemLocaleWatcher:locale: + * + * Current locale, as detected. It can be %NULL, when the locale + * was not detected yet. + * + * Since: 3.30 + **/ + g_object_class_install_property ( + object_class, + PROP_LOCALE, + g_param_spec_string ( + "locale", + "Locale", + NULL, + NULL, + G_PARAM_READABLE | + G_PARAM_STATIC_STRINGS)); +} + +static void +e_system_locale_watcher_init (ESystemLocaleWatcher *watcher) +{ + watcher->priv = G_TYPE_INSTANCE_GET_PRIVATE (watcher, E_TYPE_SYSTEM_LOCALE_WATCHER, ESystemLocaleWatcherPrivate); + + g_mutex_init (&watcher->priv->lock); + + watcher->priv->locale = NULL; +} + +/** + * e_system_locale_watcher_new: + * + * Creates a new #ESystemLocaleWatcher instance, which listens for D-Bus + * notification on locale changes. It uses system bus, unless an environment + * variable "EDS_TESTING" is defined, in which case it uses the session bus + * instead. + * + * Returns: (transfer full): a new #ESystemLocaleWatcher + * + * Since: 3.30 + **/ +ESystemLocaleWatcher * +e_system_locale_watcher_new (void) +{ + return g_object_new (E_TYPE_SYSTEM_LOCALE_WATCHER, NULL); +} + +/** + * e_system_locale_watcher_dup_locale: + * @watcher: an #ESystemLocaleWatcher + * + * Returns the current locale, as detected by the @watcher. The string + * is duplicated for thread safety. It can be %NULL, when the locale + * was not detected yet. + * + * Free it with g_free(), when no longer needed. + * + * Returns: (transfer full) (nullable): the system locale, as detected by the @watcher + * + * Since: 3.30 + **/ +gchar * +e_system_locale_watcher_dup_locale (ESystemLocaleWatcher *watcher) +{ + gchar *locale; + + g_return_val_if_fail (E_IS_SYSTEM_LOCALE_WATCHER (watcher), NULL); + + g_mutex_lock (&watcher->priv->lock); + locale = g_strdup (watcher->priv->locale); + g_mutex_unlock (&watcher->priv->lock); + + return locale; +} diff --git a/src/addressbook/libedata-book/e-system-locale-watcher.h b/src/addressbook/libedata-book/e-system-locale-watcher.h new file mode 100644 index 000000000..e1824b49c --- /dev/null +++ b/src/addressbook/libedata-book/e-system-locale-watcher.h @@ -0,0 +1,75 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ +/* + * Copyright (C) 2018 Red Hat, Inc. (www.redhat.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. + * + * 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, see <http://www.gnu.org/licenses/>. + */ + +#if !defined (__LIBEDATA_BOOK_H_INSIDE__) && !defined (LIBEDATA_BOOK_COMPILATION) +#error "Only <libedata-book/libedata-book.h> should be included directly." +#endif + +#ifndef E_SYSTEM_LOCALE_WATCHER_H +#define E_SYSTEM_LOCALE_WATCHER_H + +#include <glib-object.h> + +/* Standard GObject macros */ +#define E_TYPE_SYSTEM_LOCALE_WATCHER \ + (e_system_locale_watcher_get_type ()) +#define E_SYSTEM_LOCALE_WATCHER(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST \ + ((obj), E_TYPE_SYSTEM_LOCALE_WATCHER, ESystemLocaleWatcher)) +#define E_SYSTEM_LOCALE_WATCHER_CLASS(cls) \ + (G_TYPE_CHECK_CLASS_CAST \ + ((cls), E_TYPE_SYSTEM_LOCALE_WATCHER, ESystemLocaleWatcherClass)) +#define E_IS_SYSTEM_LOCALE_WATCHER(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE \ + ((obj), E_TYPE_SYSTEM_LOCALE_WATCHER)) +#define E_IS_SYSTEM_LOCALE_WATCHER_CLASS(cls) \ + (G_TYPE_CHECK_CLASS_TYPE \ + ((cls), E_TYPE_SYSTEM_LOCALE_WATCHER)) +#define E_SYSTEM_LOCALE_WATCHER_GET_CLASS(obj) \ + (G_TYPE_INSTANCE_GET_CLASS \ + ((obj), E_TYPE_SYSTEM_LOCALE_WATCHER, ESystemLocaleWatcherClass)) + +G_BEGIN_DECLS + +typedef struct _ESystemLocaleWatcher ESystemLocaleWatcher; +typedef struct _ESystemLocaleWatcherClass ESystemLocaleWatcherClass; +typedef struct _ESystemLocaleWatcherPrivate ESystemLocaleWatcherPrivate; + +/** + * ESystemLocaleWatcher: + * + * Contains only private data that should be read and manipulated using the + * functions below. + **/ +struct _ESystemLocaleWatcher { + /*< private >*/ + GObject parent; + ESystemLocaleWatcherPrivate *priv; +}; + +struct _ESystemLocaleWatcherClass { + GObjectClass parent_class; +}; + +GType e_system_locale_watcher_get_type (void) G_GNUC_CONST; +ESystemLocaleWatcher * + e_system_locale_watcher_new (void); +gchar * e_system_locale_watcher_dup_locale (ESystemLocaleWatcher *watcher); + +G_END_DECLS + +#endif /* E_SYSTEM_LOCALE_WATCHER_H */ diff --git a/src/addressbook/libedata-book/libedata-book.h b/src/addressbook/libedata-book/libedata-book.h index 50ee04182..b5db50376 100644 --- a/src/addressbook/libedata-book/libedata-book.h +++ b/src/addressbook/libedata-book/libedata-book.h @@ -40,6 +40,7 @@ #include <libedata-book/e-data-book-view.h> #include <libedata-book/e-data-book.h> #include <libedata-book/e-subprocess-book-factory.h> +#include <libedata-book/e-system-locale-watcher.h> #undef __LIBEDATA_BOOK_H_INSIDE__ |