Blame src/ibusregistry.c

Packit Service 1d8f1c
/* -*- mode: C; c-basic-offset: 4; indent-tabs-mode: nil; -*- */
Packit Service 1d8f1c
/* vim:set et sts=4: */
Packit Service 1d8f1c
/* bus - The Input Bus
Packit Service 1d8f1c
 * Copyright (C) 2015 Peng Huang <shawn.p.huang@gmail.com>
Packit Service 1d8f1c
 * Copyright (C) 2015-2018 Takao Fujiwara <takao.fujiwara1@gmail.com>
Packit Service 1d8f1c
 * Copyright (C) 2015 Red Hat, Inc.
Packit Service 1d8f1c
 *
Packit Service 1d8f1c
 * This library is free software; you can redistribute it and/or
Packit Service 1d8f1c
 * modify it under the terms of the GNU Lesser General Public
Packit Service 1d8f1c
 * License as published by the Free Software Foundation; either
Packit Service 1d8f1c
 * version 2.1 of the License, or (at your option) any later version.
Packit Service 1d8f1c
 *
Packit Service 1d8f1c
 * This library is distributed in the hope that it will be useful,
Packit Service 1d8f1c
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
Packit Service 1d8f1c
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
Packit Service 1d8f1c
 * Lesser General Public License for more details.
Packit Service 1d8f1c
 *
Packit Service 1d8f1c
 * You should have received a copy of the GNU Lesser General Public
Packit Service 1d8f1c
 * License along with this library; if not, write to the Free Software
Packit Service 1d8f1c
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301
Packit Service 1d8f1c
 * USA
Packit Service 1d8f1c
 */
Packit Service 1d8f1c
#include <gio/gio.h>
Packit Service 1d8f1c
#include <glib/gstdio.h>
Packit Service 1d8f1c
#include <string.h>
Packit Service 1d8f1c
Packit Service 1d8f1c
#include "ibusinternal.h"
Packit Service 1d8f1c
#include "ibusmarshalers.h"
Packit Service 1d8f1c
#include "ibusregistry.h"
Packit Service 1d8f1c
Packit Service 1d8f1c
#define IBUS_CACHE_MAGIC 0x49425553 /* "IBUS" */
Packit Service 1d8f1c
#define IBUS_CACHE_VERSION 0x00010512
Packit Service 1d8f1c
Packit Service 1d8f1c
enum {
Packit Service 1d8f1c
    CHANGED,
Packit Service 1d8f1c
    LAST_SIGNAL,
Packit Service 1d8f1c
};
Packit Service 1d8f1c
Packit Service 1d8f1c
static guint             _signals[LAST_SIGNAL] = { 0 };
Packit Service 1d8f1c
Packit Service 1d8f1c
struct _IBusRegistryPrivate {
Packit Service 1d8f1c
    /* a list of IBusObservedPath objects. */
Packit Service 1d8f1c
    GList *observed_paths;
Packit Service 1d8f1c
Packit Service 1d8f1c
    /* a list of IBusComponent objects that are created from component XML
Packit Service 1d8f1c
     * files (or from the cache of them). */
Packit Service 1d8f1c
    GList *components;
Packit Service 1d8f1c
Packit Service 1d8f1c
    gboolean changed;
Packit Service 1d8f1c
Packit Service 1d8f1c
    /* a mapping from GFile to GFileMonitor. */
Packit Service 1d8f1c
    GHashTable *monitor_table;
Packit Service 1d8f1c
Packit Service 1d8f1c
    guint monitor_timeout_id;
Packit Service 1d8f1c
};
Packit Service 1d8f1c
Packit Service 1d8f1c
#define IBUS_REGISTRY_GET_PRIVATE(o)  \
Packit Service 1d8f1c
   (G_TYPE_INSTANCE_GET_PRIVATE ((o), IBUS_TYPE_REGISTRY, IBusRegistryPrivate))
Packit Service 1d8f1c
Packit Service 1d8f1c
/* functions prototype */
Packit Service 1d8f1c
static void     ibus_registry_destroy        (IBusRegistry           *registry);
Packit Service 1d8f1c
static void     ibus_registry_remove_all     (IBusRegistry           *registry);
Packit Service 1d8f1c
static gboolean ibus_registry_serialize      (IBusRegistry           *registry,
Packit Service 1d8f1c
                                              GVariantBuilder        *builder);
Packit Service 1d8f1c
static gint     ibus_registry_deserialize    (IBusRegistry           *registry,
Packit Service 1d8f1c
                                              GVariant               *variant);
Packit Service 1d8f1c
static gboolean ibus_registry_copy           (IBusRegistry           *dest,
Packit Service 1d8f1c
                                              const IBusRegistry     *src);
Packit Service 1d8f1c
Packit Service 1d8f1c
G_DEFINE_TYPE (IBusRegistry, ibus_registry, IBUS_TYPE_SERIALIZABLE)
Packit Service 1d8f1c
Packit Service 1d8f1c
static void
Packit Service 1d8f1c
ibus_registry_class_init (IBusRegistryClass *class)
Packit Service 1d8f1c
{
Packit Service 1d8f1c
    GObjectClass *gobject_class = G_OBJECT_CLASS (class);
Packit Service 1d8f1c
    IBusObjectClass *ibus_object_class = IBUS_OBJECT_CLASS (class);
Packit Service 1d8f1c
    IBusSerializableClass *serializable_class = IBUS_SERIALIZABLE_CLASS (class);
Packit Service 1d8f1c
Packit Service 1d8f1c
    ibus_object_class->destroy = (IBusObjectDestroyFunc) ibus_registry_destroy;
Packit Service 1d8f1c
Packit Service 1d8f1c
    serializable_class->serialize =
Packit Service 1d8f1c
        (IBusSerializableSerializeFunc) ibus_registry_serialize;
Packit Service 1d8f1c
    serializable_class->deserialize =
Packit Service 1d8f1c
        (IBusSerializableDeserializeFunc) ibus_registry_deserialize;
Packit Service 1d8f1c
    serializable_class->copy = (IBusSerializableCopyFunc) ibus_registry_copy;
Packit Service 1d8f1c
Packit Service 1d8f1c
    g_type_class_add_private (class, sizeof (IBusRegistryPrivate));
Packit Service 1d8f1c
Packit Service 1d8f1c
    /* install signals */
Packit Service 1d8f1c
    /**
Packit Service 1d8f1c
     * IBusRegistry::changed:
Packit Service 1d8f1c
     * @registry: An #IBusRegistry.
Packit Service 1d8f1c
     *
Packit Service 1d8f1c
     * Emitted when any observed paths are changed.
Packit Service 1d8f1c
     * A method is not associated in this class. the "changed"
Packit Service 1d8f1c
     * signal would be handled in other classes.
Packit Service 1d8f1c
     *
Packit Service 1d8f1c
     * See also: ibus_registry_start_monitor_changes().
Packit Service 1d8f1c
     */
Packit Service 1d8f1c
    _signals[CHANGED] =
Packit Service 1d8f1c
        g_signal_new (I_("changed"),
Packit Service 1d8f1c
            G_TYPE_FROM_CLASS (gobject_class),
Packit Service 1d8f1c
            G_SIGNAL_RUN_LAST,
Packit Service 1d8f1c
            0,
Packit Service 1d8f1c
            NULL, NULL,
Packit Service 1d8f1c
            _ibus_marshal_VOID__VOID,
Packit Service 1d8f1c
            G_TYPE_NONE,
Packit Service 1d8f1c
            0);
Packit Service 1d8f1c
}
Packit Service 1d8f1c
Packit Service 1d8f1c
static void
Packit Service 1d8f1c
ibus_registry_init (IBusRegistry *registry)
Packit Service 1d8f1c
{
Packit Service 1d8f1c
    registry->priv = IBUS_REGISTRY_GET_PRIVATE (registry);
Packit Service 1d8f1c
Packit Service 1d8f1c
    registry->priv->observed_paths = NULL;
Packit Service 1d8f1c
    registry->priv->components = NULL;
Packit Service 1d8f1c
    registry->priv->changed = FALSE;
Packit Service 1d8f1c
    registry->priv->monitor_table =
Packit Service 1d8f1c
        g_hash_table_new_full (g_file_hash,
Packit Service 1d8f1c
                               (GEqualFunc) g_file_equal,
Packit Service 1d8f1c
                               (GDestroyNotify) g_object_unref,
Packit Service 1d8f1c
                               (GDestroyNotify) g_object_unref);
Packit Service 1d8f1c
}
Packit Service 1d8f1c
Packit Service 1d8f1c
static void
Packit Service 1d8f1c
ibus_registry_destroy (IBusRegistry *registry)
Packit Service 1d8f1c
{
Packit Service 1d8f1c
    ibus_registry_remove_all (registry);
Packit Service 1d8f1c
Packit Service 1d8f1c
    g_hash_table_destroy (registry->priv->monitor_table);
Packit Service 1d8f1c
    registry->priv->monitor_table = NULL;
Packit Service 1d8f1c
Packit Service 1d8f1c
    if (registry->priv->monitor_timeout_id > 0) {
Packit Service 1d8f1c
        g_source_remove (registry->priv->monitor_timeout_id);
Packit Service 1d8f1c
        registry->priv->monitor_timeout_id = 0;
Packit Service 1d8f1c
    }
Packit Service 1d8f1c
Packit Service 1d8f1c
    IBUS_OBJECT_CLASS (ibus_registry_parent_class)->
Packit Service 1d8f1c
            destroy (IBUS_OBJECT (registry));
Packit Service 1d8f1c
}
Packit Service 1d8f1c
Packit Service 1d8f1c
static gboolean
Packit Service 1d8f1c
ibus_registry_serialize (IBusRegistry    *registry,
Packit Service 1d8f1c
                         GVariantBuilder *builder)
Packit Service 1d8f1c
{
Packit Service 1d8f1c
    gboolean retval;
Packit Service 1d8f1c
Packit Service 1d8f1c
    retval = IBUS_SERIALIZABLE_CLASS (ibus_registry_parent_class)->
Packit Service 1d8f1c
        serialize ((IBusSerializable *)registry, builder);
Packit Service 1d8f1c
    g_return_val_if_fail (retval, FALSE);
Packit Service 1d8f1c
Packit Service 1d8f1c
    GList *p;
Packit Service 1d8f1c
    GVariantBuilder *array;
Packit Service 1d8f1c
Packit Service 1d8f1c
    array = g_variant_builder_new (G_VARIANT_TYPE ("av"));
Packit Service 1d8f1c
    for (p = registry->priv->observed_paths; p != NULL; p = p->next) {
Packit Service 1d8f1c
        IBusSerializable *serializable = (IBusSerializable *) p->data;
Packit Service 1d8f1c
        g_variant_builder_add (array,
Packit Service 1d8f1c
                               "v",
Packit Service 1d8f1c
                               ibus_serializable_serialize (serializable));
Packit Service 1d8f1c
    }
Packit Service 1d8f1c
    g_variant_builder_add (builder, "av", array);
Packit Service 1d8f1c
    g_variant_builder_unref (array);
Packit Service 1d8f1c
Packit Service 1d8f1c
    array = g_variant_builder_new (G_VARIANT_TYPE ("av"));
Packit Service 1d8f1c
    for (p = registry->priv->components; p != NULL; p = p->next) {
Packit Service 1d8f1c
        IBusSerializable *serializable = (IBusSerializable *) p->data;
Packit Service 1d8f1c
        g_variant_builder_add (array,
Packit Service 1d8f1c
                               "v",
Packit Service 1d8f1c
                               ibus_serializable_serialize (serializable));
Packit Service 1d8f1c
    }
Packit Service 1d8f1c
    g_variant_builder_add (builder, "av", array);
Packit Service 1d8f1c
    g_variant_builder_unref (array);
Packit Service 1d8f1c
Packit Service 1d8f1c
    return TRUE;
Packit Service 1d8f1c
}
Packit Service 1d8f1c
Packit Service 1d8f1c
static gint
Packit Service 1d8f1c
ibus_registry_deserialize (IBusRegistry *registry,
Packit Service 1d8f1c
                           GVariant     *variant)
Packit Service 1d8f1c
{
Packit Service 1d8f1c
    GVariant *var;
Packit Service 1d8f1c
    GVariantIter *iter;
Packit Service 1d8f1c
    gint retval;
Packit Service 1d8f1c
Packit Service 1d8f1c
    retval = IBUS_SERIALIZABLE_CLASS (ibus_registry_parent_class)->
Packit Service 1d8f1c
        deserialize ((IBusSerializable *)registry, variant);
Packit Service 1d8f1c
    g_return_val_if_fail (retval, 0);
Packit Service 1d8f1c
Packit Service 1d8f1c
    g_variant_get_child (variant, retval++, "av", &iter);
Packit Service 1d8f1c
    while (g_variant_iter_loop (iter, "v", &var)) {
Packit Service 1d8f1c
        IBusSerializable *serializable = ibus_serializable_deserialize (var);
Packit Service 1d8f1c
        registry->priv->observed_paths =
Packit Service 1d8f1c
            g_list_append (registry->priv->observed_paths,
Packit Service 1d8f1c
                           IBUS_OBSERVED_PATH (serializable));
Packit Service 1d8f1c
    }
Packit Service 1d8f1c
    g_variant_iter_free (iter);
Packit Service 1d8f1c
Packit Service 1d8f1c
    g_variant_get_child (variant, retval++, "av", &iter);
Packit Service 1d8f1c
    while (g_variant_iter_loop (iter, "v", &var)) {
Packit Service 1d8f1c
        IBusSerializable *serializable = ibus_serializable_deserialize (var);
Packit Service 1d8f1c
        registry->priv->components =
Packit Service 1d8f1c
            g_list_append (registry->priv->components,
Packit Service 1d8f1c
                           IBUS_COMPONENT (serializable));
Packit Service 1d8f1c
    }
Packit Service 1d8f1c
    g_variant_iter_free (iter);
Packit Service 1d8f1c
Packit Service 1d8f1c
    return retval;
Packit Service 1d8f1c
}
Packit Service 1d8f1c
Packit Service 1d8f1c
static gboolean
Packit Service 1d8f1c
ibus_registry_copy (IBusRegistry       *dest,
Packit Service 1d8f1c
                    const IBusRegistry *src)
Packit Service 1d8f1c
{
Packit Service 1d8f1c
    gboolean retval;
Packit Service 1d8f1c
Packit Service 1d8f1c
    retval = IBUS_SERIALIZABLE_CLASS (ibus_registry_parent_class)->
Packit Service 1d8f1c
        copy ((IBusSerializable *)dest, (IBusSerializable *)src);
Packit Service 1d8f1c
    g_return_val_if_fail (retval, FALSE);
Packit Service 1d8f1c
Packit Service 1d8f1c
    dest->priv->components = g_list_copy (src->priv->components);
Packit Service 1d8f1c
    dest->priv->observed_paths = g_list_copy (src->priv->observed_paths);
Packit Service 1d8f1c
Packit Service 1d8f1c
    return TRUE;
Packit Service 1d8f1c
}
Packit Service 1d8f1c
Packit Service 1d8f1c
/**
Packit Service 1d8f1c
 * ibus_registry_remove_all:
Packit Service 1d8f1c
 *
Packit Service 1d8f1c
 * Remove the loaded registry.
Packit Service 1d8f1c
 */
Packit Service 1d8f1c
static void
Packit Service 1d8f1c
ibus_registry_remove_all (IBusRegistry *registry)
Packit Service 1d8f1c
{
Packit Service 1d8f1c
    g_assert (IBUS_IS_REGISTRY (registry));
Packit Service 1d8f1c
Packit Service 1d8f1c
    g_list_free_full (registry->priv->observed_paths, g_object_unref);
Packit Service 1d8f1c
    registry->priv->observed_paths = NULL;
Packit Service 1d8f1c
Packit Service 1d8f1c
    g_list_free_full (registry->priv->components, g_object_unref);
Packit Service 1d8f1c
    registry->priv->components = NULL;
Packit Service 1d8f1c
}
Packit Service 1d8f1c
Packit Service 1d8f1c
void
Packit Service 1d8f1c
ibus_registry_load (IBusRegistry *registry)
Packit Service 1d8f1c
{
Packit Service 1d8f1c
    const gchar *envstr;
Packit Service 1d8f1c
    GPtrArray *path;
Packit Service 1d8f1c
    gchar **d, **search_path;
Packit Service 1d8f1c
Packit Service 1d8f1c
    g_assert (IBUS_IS_REGISTRY (registry));
Packit Service 1d8f1c
Packit Service 1d8f1c
    path = g_ptr_array_new();
Packit Service 1d8f1c
Packit Service 1d8f1c
    envstr = g_getenv ("IBUS_COMPONENT_PATH");
Packit Service 1d8f1c
    if (envstr) {
Packit Service 1d8f1c
        gchar **dirs = g_strsplit (envstr, G_SEARCHPATH_SEPARATOR_S, 0);
Packit Service 1d8f1c
        for (d = dirs; *d != NULL; d++)
Packit Service 1d8f1c
            g_ptr_array_add (path, *d);
Packit Service 1d8f1c
        g_free (dirs);
Packit Service 1d8f1c
    } else {
Packit Service 1d8f1c
        gchar *dirname;
Packit Service 1d8f1c
Packit Service 1d8f1c
        dirname = g_build_filename (IBUS_DATA_DIR, "component", NULL);
Packit Service 1d8f1c
        g_ptr_array_add (path, dirname);
Packit Service 1d8f1c
Packit Service 1d8f1c
#if 0
Packit Service 1d8f1c
        /* FIXME Should we support install some IME in user dir? */
Packit Service 1d8f1c
        dirname = g_build_filename (g_get_user_data_dir (),
Packit Service 1d8f1c
                                    "ibus", "component",
Packit Service 1d8f1c
                                    NULL);
Packit Service 1d8f1c
        g_ptr_array_add (path, dirname);
Packit Service 1d8f1c
#endif
Packit Service 1d8f1c
    }
Packit Service 1d8f1c
Packit Service 1d8f1c
    g_ptr_array_add (path, NULL);
Packit Service 1d8f1c
    search_path = (gchar **) g_ptr_array_free (path, FALSE);
Packit Service 1d8f1c
    for (d = search_path; *d != NULL; d++) {
Packit Service 1d8f1c
        ibus_registry_load_in_dir (registry, *d);
Packit Service 1d8f1c
    }
Packit Service 1d8f1c
    g_strfreev (search_path);
Packit Service 1d8f1c
}
Packit Service 1d8f1c
Packit Service 1d8f1c
gboolean
Packit Service 1d8f1c
ibus_registry_load_cache (IBusRegistry *registry,
Packit Service 1d8f1c
                          gboolean      is_user)
Packit Service 1d8f1c
{
Packit Service 1d8f1c
    gchar *filename;
Packit Service 1d8f1c
    gboolean retval;
Packit Service 1d8f1c
Packit Service 1d8f1c
    g_assert (IBUS_IS_REGISTRY (registry));
Packit Service 1d8f1c
Packit Service 1d8f1c
    if (is_user) {
Packit Service 1d8f1c
        filename = g_build_filename (g_get_user_cache_dir (),
Packit Service 1d8f1c
                                     "ibus", "bus", "registry", NULL);
Packit Service 1d8f1c
    } else {
Packit Service 1d8f1c
        filename = g_build_filename (IBUS_CACHE_DIR,
Packit Service 1d8f1c
                                     "bus", "registry", NULL);
Packit Service 1d8f1c
    }
Packit Service 1d8f1c
Packit Service 1d8f1c
    retval = ibus_registry_load_cache_file (registry, filename);
Packit Service 1d8f1c
    g_free (filename);
Packit Service 1d8f1c
Packit Service 1d8f1c
    return retval;
Packit Service 1d8f1c
}
Packit Service 1d8f1c
Packit Service 1d8f1c
gboolean
Packit Service 1d8f1c
ibus_registry_load_cache_file (IBusRegistry *registry,
Packit Service 1d8f1c
                               const gchar  *filename)
Packit Service 1d8f1c
{
Packit Service 1d8f1c
    gchar *contents, *p;
Packit Service 1d8f1c
    gsize length;
Packit Service 1d8f1c
    GVariant *variant;
Packit Service 1d8f1c
    GError *error;
Packit Service 1d8f1c
Packit Service 1d8f1c
    g_assert (IBUS_IS_REGISTRY (registry));
Packit Service 1d8f1c
    g_assert (filename != NULL);
Packit Service 1d8f1c
Packit Service 1d8f1c
    if (!g_file_test (filename, G_FILE_TEST_EXISTS))
Packit Service 1d8f1c
        return FALSE;
Packit Service 1d8f1c
Packit Service 1d8f1c
    error = NULL;
Packit Service 1d8f1c
    if (!g_file_get_contents (filename, &contents, &length, &error)) {
Packit Service 1d8f1c
        g_warning ("cannot read %s: %s", filename, error->message);
Packit Service 1d8f1c
        g_error_free (error);
Packit Service 1d8f1c
        return FALSE;
Packit Service 1d8f1c
    }
Packit Service 1d8f1c
Packit Service 1d8f1c
    p = contents;
Packit Service 1d8f1c
Packit Service 1d8f1c
    /* read file header including magic and version */
Packit Service 1d8f1c
    if (length < 8) {
Packit Service 1d8f1c
        g_free (contents);
Packit Service 1d8f1c
        return FALSE;
Packit Service 1d8f1c
    }
Packit Service 1d8f1c
Packit Service 1d8f1c
    if (GUINT32_FROM_BE (*(guint32 *) p) != IBUS_CACHE_MAGIC) {
Packit Service 1d8f1c
        g_free (contents);
Packit Service 1d8f1c
        return FALSE;
Packit Service 1d8f1c
    }
Packit Service 1d8f1c
    p += 4;
Packit Service 1d8f1c
Packit Service 1d8f1c
    if (GUINT32_FROM_BE (*(guint32 *) p) != IBUS_CACHE_VERSION) {
Packit Service 1d8f1c
        g_free (contents);
Packit Service 1d8f1c
        return FALSE;
Packit Service 1d8f1c
    }
Packit Service 1d8f1c
    p += 4;
Packit Service 1d8f1c
Packit Service 1d8f1c
    /* read serialized IBusRegistry */
Packit Service 1d8f1c
    variant = g_variant_new_from_data (G_VARIANT_TYPE ("(sa{sv}avav)"),
Packit Service 1d8f1c
                                       p,
Packit Service 1d8f1c
                                       length - (p - contents),
Packit Service 1d8f1c
                                       FALSE,
Packit Service 1d8f1c
                                       (GDestroyNotify) g_free,
Packit Service 1d8f1c
                                       NULL);
Packit Service 1d8f1c
    if (variant == NULL) {
Packit Service 1d8f1c
        g_free (contents);
Packit Service 1d8f1c
        return FALSE;
Packit Service 1d8f1c
    }
Packit Service 1d8f1c
Packit Service 1d8f1c
    ibus_registry_deserialize (registry, variant);
Packit Service 1d8f1c
    g_variant_unref (variant);
Packit Service 1d8f1c
    g_free (contents);
Packit Service 1d8f1c
Packit Service 1d8f1c
    return TRUE;
Packit Service 1d8f1c
}
Packit Service 1d8f1c
Packit Service 1d8f1c
gboolean
Packit Service 1d8f1c
ibus_registry_check_modification (IBusRegistry *registry)
Packit Service 1d8f1c
{
Packit Service 1d8f1c
    GList *p;
Packit Service 1d8f1c
Packit Service 1d8f1c
    g_assert (IBUS_IS_REGISTRY (registry));
Packit Service 1d8f1c
Packit Service 1d8f1c
    for (p = registry->priv->observed_paths; p != NULL; p = p->next) {
Packit Service 1d8f1c
        if (!IBUS_IS_OBSERVED_PATH (p->data)) {
Packit Service 1d8f1c
            g_warning ("The registry cache of observed_paths might be " \
Packit Service 1d8f1c
                       "broken and have to generate the cache again.");
Packit Service 1d8f1c
            g_list_free_full (registry->priv->observed_paths, g_object_unref);
Packit Service 1d8f1c
            registry->priv->observed_paths = NULL;
Packit Service 1d8f1c
            return TRUE;
Packit Service 1d8f1c
        }
Packit Service 1d8f1c
        if (ibus_observed_path_check_modification (
Packit Service 1d8f1c
                    (IBusObservedPath *) p->data))
Packit Service 1d8f1c
            return TRUE;
Packit Service 1d8f1c
    }
Packit Service 1d8f1c
Packit Service 1d8f1c
    for (p = registry->priv->components; p != NULL; p = p->next) {
Packit Service 1d8f1c
        if (!IBUS_IS_COMPONENT (p->data)) {
Packit Service 1d8f1c
            g_warning ("The registry cache of components might be " \
Packit Service 1d8f1c
                       "broken and have to generate the cache again.");
Packit Service 1d8f1c
            g_list_free_full (registry->priv->components, g_object_unref);
Packit Service 1d8f1c
            registry->priv->components = NULL;
Packit Service 1d8f1c
            return TRUE;
Packit Service 1d8f1c
        }
Packit Service 1d8f1c
        if (ibus_component_check_modification ((IBusComponent *) p->data))
Packit Service 1d8f1c
            return TRUE;
Packit Service 1d8f1c
    }
Packit Service 1d8f1c
Packit Service 1d8f1c
    return FALSE;
Packit Service 1d8f1c
}
Packit Service 1d8f1c
Packit Service 1d8f1c
gboolean
Packit Service 1d8f1c
ibus_registry_save_cache (IBusRegistry *registry,
Packit Service 1d8f1c
                          gboolean      is_user)
Packit Service 1d8f1c
{
Packit Service 1d8f1c
    gchar *filename;
Packit Service 1d8f1c
    gboolean retval;
Packit Service 1d8f1c
Packit Service 1d8f1c
    g_assert (IBUS_IS_REGISTRY (registry));
Packit Service 1d8f1c
Packit Service 1d8f1c
    if (is_user) {
Packit Service 1d8f1c
        filename = g_build_filename (g_get_user_cache_dir (),
Packit Service 1d8f1c
                                     "ibus", "bus", "registry", NULL);
Packit Service 1d8f1c
    } else {
Packit Service 1d8f1c
        filename = g_build_filename (IBUS_CACHE_DIR,
Packit Service 1d8f1c
                                     "bus", "registry", NULL);
Packit Service 1d8f1c
    }
Packit Service 1d8f1c
Packit Service 1d8f1c
    retval = ibus_registry_save_cache_file (registry, filename);
Packit Service 1d8f1c
    g_free (filename);
Packit Service 1d8f1c
Packit Service 1d8f1c
    return retval;
Packit Service 1d8f1c
}
Packit Service 1d8f1c
Packit Service 1d8f1c
gboolean
Packit Service 1d8f1c
ibus_registry_save_cache_file (IBusRegistry *registry,
Packit Service 1d8f1c
                               const gchar  *filename)
Packit Service 1d8f1c
{
Packit Service 1d8f1c
    gchar *cachedir;
Packit Service 1d8f1c
    GVariant *variant;
Packit Service 1d8f1c
    gchar *contents, *p;
Packit Service 1d8f1c
    gsize length;
Packit Service 1d8f1c
    gboolean retval;
Packit Service 1d8f1c
    guint32 intval;
Packit Service 1d8f1c
    GError *error;
Packit Service 1d8f1c
Packit Service 1d8f1c
    g_assert (IBUS_IS_REGISTRY (registry));
Packit Service 1d8f1c
    g_assert (filename != NULL);
Packit Service 1d8f1c
Packit Service 1d8f1c
    cachedir = g_path_get_dirname (filename);
Packit Service 1d8f1c
    g_mkdir_with_parents (cachedir, 0775);
Packit Service 1d8f1c
    g_free (cachedir);
Packit Service 1d8f1c
Packit Service 1d8f1c
    variant = ibus_serializable_serialize (IBUS_SERIALIZABLE (registry));
Packit Service 1d8f1c
    length = 8 + g_variant_get_size (variant);
Packit Service 1d8f1c
    p = contents = g_slice_alloc (length);
Packit Service 1d8f1c
Packit Service 1d8f1c
    /* write file header */
Packit Service 1d8f1c
    intval = GUINT32_TO_BE (IBUS_CACHE_MAGIC);
Packit Service 1d8f1c
    memcpy (p, (gchar *) &intval, 4);
Packit Service 1d8f1c
    p += 4;
Packit Service 1d8f1c
Packit Service 1d8f1c
    intval = GUINT32_TO_BE (IBUS_CACHE_VERSION);
Packit Service 1d8f1c
    memcpy (p, (gchar *) &intval, 4);
Packit Service 1d8f1c
    p += 4;
Packit Service 1d8f1c
Packit Service 1d8f1c
    /* write serialized IBusRegistry */
Packit Service 1d8f1c
    g_variant_store (variant, p);
Packit Service 1d8f1c
Packit Service 1d8f1c
    error = NULL;
Packit Service 1d8f1c
    retval = g_file_set_contents (filename, contents, length, &error);
Packit Service 1d8f1c
Packit Service 1d8f1c
    g_variant_unref (variant);
Packit Service 1d8f1c
    g_slice_free1 (length, contents);
Packit Service 1d8f1c
Packit Service 1d8f1c
    if (!retval) {
Packit Service 1d8f1c
        g_warning ("cannot write %s: %s", filename, error->message);
Packit Service 1d8f1c
        g_error_free (error);
Packit Service 1d8f1c
        return FALSE;
Packit Service 1d8f1c
    }
Packit Service 1d8f1c
Packit Service 1d8f1c
    if (g_str_has_prefix (filename, g_get_user_cache_dir ())) {
Packit Service 1d8f1c
        g_warn_if_fail (!g_chmod (filename, 0644));
Packit Service 1d8f1c
    }
Packit Service 1d8f1c
Packit Service 1d8f1c
    return TRUE;
Packit Service 1d8f1c
}
Packit Service 1d8f1c
Packit Service 1d8f1c
#define g_string_append_indent(string, indent)  \
Packit Service 1d8f1c
    {                                           \
Packit Service 1d8f1c
        gint i;                                 \
Packit Service 1d8f1c
        for (i = 0; i < (indent); i++) {        \
Packit Service 1d8f1c
            g_string_append (string, "    ");   \
Packit Service 1d8f1c
        }                                       \
Packit Service 1d8f1c
    }
Packit Service 1d8f1c
Packit Service 1d8f1c
void
Packit Service 1d8f1c
ibus_registry_output (IBusRegistry *registry,
Packit Service 1d8f1c
                      GString      *output,
Packit Service 1d8f1c
                      int           indent)
Packit Service 1d8f1c
{
Packit Service 1d8f1c
    GList *p;
Packit Service 1d8f1c
Packit Service 1d8f1c
    g_assert (IBUS_IS_REGISTRY (registry));
Packit Service 1d8f1c
    g_return_if_fail (output != NULL);
Packit Service 1d8f1c
Packit Service 1d8f1c
    g_string_append (output, "\n");
Packit Service 1d8f1c
    g_string_append (output, "<ibus-registry>\n");
Packit Service 1d8f1c
Packit Service 1d8f1c
    if (registry->priv->observed_paths) {
Packit Service 1d8f1c
        g_string_append_indent (output, indent);
Packit Service 1d8f1c
        g_string_append (output, "<observed-paths>\n");
Packit Service 1d8f1c
        for (p = registry->priv->observed_paths; p != NULL; p = p->next) {
Packit Service 1d8f1c
            ibus_observed_path_output ((IBusObservedPath *) p->data,
Packit Service 1d8f1c
                                      output, indent * 2);
Packit Service 1d8f1c
        }
Packit Service 1d8f1c
        g_string_append_indent (output, indent);
Packit Service 1d8f1c
        g_string_append (output, "</observed-paths>\n");
Packit Service 1d8f1c
    }
Packit Service 1d8f1c
Packit Service 1d8f1c
    if (registry->priv->components) {
Packit Service 1d8f1c
        g_string_append_indent (output, indent);
Packit Service 1d8f1c
        g_string_append (output, "<components>\n");
Packit Service 1d8f1c
        for (p = registry->priv->components; p != NULL; p = p->next) {
Packit Service 1d8f1c
            ibus_component_output ((IBusComponent *) p->data,
Packit Service 1d8f1c
                                   output, indent * 2);
Packit Service 1d8f1c
        }
Packit Service 1d8f1c
        g_string_append_indent (output, indent);
Packit Service 1d8f1c
        g_string_append (output, "</components>\n");
Packit Service 1d8f1c
    }
Packit Service 1d8f1c
Packit Service 1d8f1c
    g_string_append (output, "</ibus-registry>\n");
Packit Service 1d8f1c
}
Packit Service 1d8f1c
Packit Service 1d8f1c
void
Packit Service 1d8f1c
ibus_registry_load_in_dir (IBusRegistry *registry,
Packit Service 1d8f1c
                           const gchar  *dirname)
Packit Service 1d8f1c
{
Packit Service 1d8f1c
    GError *error = NULL;
Packit Service 1d8f1c
    GDir *dir;
Packit Service 1d8f1c
    IBusObservedPath *observed_path = NULL;
Packit Service 1d8f1c
    const gchar *filename;
Packit Service 1d8f1c
Packit Service 1d8f1c
    g_assert (IBUS_IS_REGISTRY (registry));
Packit Service 1d8f1c
    g_assert (dirname);
Packit Service 1d8f1c
Packit Service 1d8f1c
    dir = g_dir_open (dirname, 0, &error);
Packit Service 1d8f1c
Packit Service 1d8f1c
    if (dir == NULL) {
Packit Service 1d8f1c
        g_warning ("Unable open directory %s : %s", dirname, error->message);
Packit Service 1d8f1c
        g_error_free (error);
Packit Service 1d8f1c
        return;
Packit Service 1d8f1c
    }
Packit Service 1d8f1c
Packit Service 1d8f1c
    observed_path = ibus_observed_path_new (dirname, TRUE);
Packit Service 1d8f1c
Packit Service 1d8f1c
    registry->priv->observed_paths =
Packit Service 1d8f1c
            g_list_append (registry->priv->observed_paths,
Packit Service 1d8f1c
                           observed_path);
Packit Service 1d8f1c
Packit Service 1d8f1c
    while ((filename = g_dir_read_name (dir)) != NULL) {
Packit Service 1d8f1c
        glong size;
Packit Service 1d8f1c
        gchar *path;
Packit Service 1d8f1c
        IBusComponent *component;
Packit Service 1d8f1c
Packit Service 1d8f1c
        size = g_utf8_strlen (filename, -1);
Packit Service 1d8f1c
        if (g_strcmp0 (MAX (filename, filename + size - 4), ".xml") != 0)
Packit Service 1d8f1c
            continue;
Packit Service 1d8f1c
Packit Service 1d8f1c
        path = g_build_filename (dirname, filename, NULL);
Packit Service 1d8f1c
        component = ibus_component_new_from_file (path);
Packit Service 1d8f1c
        if (component != NULL) {
Packit Service 1d8f1c
            g_object_ref_sink (component);
Packit Service 1d8f1c
            registry->priv->components =
Packit Service 1d8f1c
                g_list_append (registry->priv->components, component);
Packit Service 1d8f1c
        }
Packit Service 1d8f1c
Packit Service 1d8f1c
        g_free (path);
Packit Service 1d8f1c
    }
Packit Service 1d8f1c
Packit Service 1d8f1c
    g_dir_close (dir);
Packit Service 1d8f1c
}
Packit Service 1d8f1c
Packit Service 1d8f1c
Packit Service 1d8f1c
IBusRegistry *
Packit Service 1d8f1c
ibus_registry_new (void)
Packit Service 1d8f1c
{
Packit Service 1d8f1c
    IBusRegistry *registry;
Packit Service 1d8f1c
    registry = (IBusRegistry *) g_object_new (IBUS_TYPE_REGISTRY, NULL);
Packit Service 1d8f1c
    return registry;
Packit Service 1d8f1c
}
Packit Service 1d8f1c
Packit Service 1d8f1c
GList *
Packit Service 1d8f1c
ibus_registry_get_components (IBusRegistry *registry)
Packit Service 1d8f1c
{
Packit Service 1d8f1c
    g_assert (IBUS_IS_REGISTRY (registry));
Packit Service 1d8f1c
Packit Service 1d8f1c
    return g_list_copy (registry->priv->components);
Packit Service 1d8f1c
}
Packit Service 1d8f1c
Packit Service 1d8f1c
GList *
Packit Service 1d8f1c
ibus_registry_get_observed_paths (IBusRegistry *registry)
Packit Service 1d8f1c
{
Packit Service 1d8f1c
    g_assert (IBUS_IS_REGISTRY (registry));
Packit Service 1d8f1c
Packit Service 1d8f1c
    return g_list_copy (registry->priv->observed_paths);
Packit Service 1d8f1c
}
Packit Service 1d8f1c
Packit Service 1d8f1c
static gboolean
Packit Service 1d8f1c
_monitor_timeout_cb (IBusRegistry *registry)
Packit Service 1d8f1c
{
Packit Service 1d8f1c
    g_hash_table_remove_all (registry->priv->monitor_table);
Packit Service 1d8f1c
    registry->priv->changed = TRUE;
Packit Service 1d8f1c
    g_signal_emit (registry, _signals[CHANGED], 0);
Packit Service 1d8f1c
    registry->priv->monitor_timeout_id = 0;
Packit Service 1d8f1c
    return FALSE;
Packit Service 1d8f1c
}
Packit Service 1d8f1c
Packit Service 1d8f1c
static void
Packit Service 1d8f1c
_monitor_changed_cb (GFileMonitor     *monitor,
Packit Service 1d8f1c
                     GFile            *file,
Packit Service 1d8f1c
                     GFile            *other_file,
Packit Service 1d8f1c
                     GFileMonitorEvent event_type,
Packit Service 1d8f1c
                     IBusRegistry     *registry)
Packit Service 1d8f1c
{
Packit Service 1d8f1c
    g_assert (IBUS_IS_REGISTRY (registry));
Packit Service 1d8f1c
Packit Service 1d8f1c
    if (event_type != G_FILE_MONITOR_EVENT_CHANGED &&
Packit Service 1d8f1c
        event_type != G_FILE_MONITOR_EVENT_DELETED &&
Packit Service 1d8f1c
        event_type != G_FILE_MONITOR_EVENT_CREATED &&
Packit Service 1d8f1c
        event_type != G_FILE_MONITOR_EVENT_ATTRIBUTE_CHANGED)
Packit Service 1d8f1c
        return;
Packit Service 1d8f1c
Packit Service 1d8f1c
    /* Merge successive file changes into one, with a low priority
Packit Service 1d8f1c
       timeout handler. */
Packit Service 1d8f1c
    if (registry->priv->monitor_timeout_id > 0)
Packit Service 1d8f1c
        return;
Packit Service 1d8f1c
Packit Service 1d8f1c
    registry->priv->monitor_timeout_id =
Packit Service 1d8f1c
        g_timeout_add_full (G_PRIORITY_DEFAULT_IDLE,
Packit Service 1d8f1c
                            5000,
Packit Service 1d8f1c
                            (GSourceFunc) _monitor_timeout_cb,
Packit Service 1d8f1c
                            g_object_ref (registry),
Packit Service 1d8f1c
                            (GDestroyNotify) g_object_unref);
Packit Service 1d8f1c
}
Packit Service 1d8f1c
Packit Service 1d8f1c
void
Packit Service 1d8f1c
ibus_registry_start_monitor_changes (IBusRegistry *registry)
Packit Service 1d8f1c
{
Packit Service 1d8f1c
    GList *observed_paths, *p;
Packit Service 1d8f1c
Packit Service 1d8f1c
    g_assert (IBUS_IS_REGISTRY (registry));
Packit Service 1d8f1c
Packit Service 1d8f1c
    g_hash_table_remove_all (registry->priv->monitor_table);
Packit Service 1d8f1c
Packit Service 1d8f1c
    observed_paths = g_list_copy (registry->priv->observed_paths);
Packit Service 1d8f1c
    for (p = registry->priv->components; p != NULL; p = p->next) {
Packit Service 1d8f1c
        IBusComponent *component = (IBusComponent *) p->data;
Packit Service 1d8f1c
        GList *component_observed_paths =
Packit Service 1d8f1c
            ibus_component_get_observed_paths (component);
Packit Service 1d8f1c
        observed_paths = g_list_concat (observed_paths,
Packit Service 1d8f1c
                                        component_observed_paths);
Packit Service 1d8f1c
    }
Packit Service 1d8f1c
Packit Service 1d8f1c
    for (p = observed_paths; p != NULL; p = p->next) {
Packit Service 1d8f1c
        IBusObservedPath *path = (IBusObservedPath *) p->data;
Packit Service 1d8f1c
        GFile *file = g_file_new_for_path (path->path);
Packit Service 1d8f1c
        if (g_hash_table_lookup (registry->priv->monitor_table, file) == NULL) {
Packit Service 1d8f1c
            GFileMonitor *monitor;
Packit Service 1d8f1c
            GError *error;
Packit Service 1d8f1c
Packit Service 1d8f1c
            error = NULL;
Packit Service 1d8f1c
            monitor = g_file_monitor (file,
Packit Service 1d8f1c
                                      G_FILE_MONITOR_NONE,
Packit Service 1d8f1c
                                      NULL,
Packit Service 1d8f1c
                                      &error);
Packit Service 1d8f1c
Packit Service 1d8f1c
            if (monitor != NULL) {
Packit Service 1d8f1c
                g_signal_connect (monitor, "changed",
Packit Service 1d8f1c
                                  G_CALLBACK (_monitor_changed_cb),
Packit Service 1d8f1c
                                  registry);
Packit Service 1d8f1c
Packit Service 1d8f1c
                g_hash_table_replace (registry->priv->monitor_table,
Packit Service 1d8f1c
                                      g_object_ref (file),
Packit Service 1d8f1c
                                      monitor);
Packit Service 1d8f1c
            } else {
Packit Service 1d8f1c
                g_warning ("Can't monitor directory %s: %s",
Packit Service 1d8f1c
                           path->path,
Packit Service 1d8f1c
                           error->message);
Packit Service 1d8f1c
                g_error_free (error);
Packit Service 1d8f1c
            }
Packit Service 1d8f1c
        }
Packit Service 1d8f1c
        g_object_unref (file);
Packit Service 1d8f1c
    }
Packit Service 1d8f1c
    g_list_free (observed_paths);
Packit Service 1d8f1c
}