summaryrefslogtreecommitdiff
path: root/src/addressbook/libedata-book
diff options
context:
space:
mode:
authorMilan Crha <mcrha@redhat.com>2018-03-12 14:06:44 +0100
committerMilan Crha <mcrha@redhat.com>2018-03-12 14:06:44 +0100
commit7ca206e0c0044bb6f85b8421562f2e42002f6283 (patch)
treeda32f37416626cfed6e95941354b3eea1a73b783 /src/addressbook/libedata-book
parentd55d0735657d02b156da7664ed40b5f36cb3b5af (diff)
downloadevolution-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.txt2
-rw-r--r--src/addressbook/libedata-book/e-data-book-factory.c179
-rw-r--r--src/addressbook/libedata-book/e-subprocess-book-factory.c272
-rw-r--r--src/addressbook/libedata-book/e-system-locale-watcher.c365
-rw-r--r--src/addressbook/libedata-book/e-system-locale-watcher.h75
-rw-r--r--src/addressbook/libedata-book/libedata-book.h1
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__