1 /* -*- mode: C; c-basic-offset: 4; indent-tabs-mode: nil; -*- */
2 /* vim:set et sts=4: */
4 * Copyright (C) 2013 Peng Huang <shawn.p.huang@gmail.com>
5 * Copyright (C) 2013 Takao Fujiwara <takao.fujiwara1@gmail.com>
6 * Copyright (C) 2013 Red Hat, Inc.
8 * This library is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Lesser General Public
10 * License as published by the Free Software Foundation; either
11 * version 2 of the License, or (at your option) any later version.
13 * This library is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * Lesser General Public License for more details.
18 * You should have received a copy of the GNU Lesser General Public
19 * License along with this library; if not, write to the
20 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
21 * Boston, MA 02111-1307, USA.
24 #include <glib/gstdio.h>
27 #include "ibusinternal.h"
28 #include "ibusmarshalers.h"
29 #include "ibusregistry.h"
31 #define IBUS_CACHE_MAGIC 0x49425553 /* "IBUS" */
32 #define IBUS_CACHE_VERSION 0x00010502
39 static guint _signals[LAST_SIGNAL] = { 0 };
41 struct _IBusRegistryPrivate {
42 /* a list of IBusObservedPath objects. */
43 GList *observed_paths;
45 /* a list of IBusComponent objects that are created from component XML
46 * files (or from the cache of them). */
51 /* a mapping from GFile to GFileMonitor. */
52 GHashTable *monitor_table;
54 guint monitor_timeout_id;
57 #define IBUS_REGISTRY_GET_PRIVATE(o) \
58 (G_TYPE_INSTANCE_GET_PRIVATE ((o), IBUS_TYPE_REGISTRY, IBusRegistryPrivate))
60 /* functions prototype */
61 static void ibus_registry_destroy (IBusRegistry *registry);
62 static void ibus_registry_remove_all (IBusRegistry *registry);
63 static gboolean ibus_registry_serialize (IBusRegistry *registry,
64 GVariantBuilder *builder);
65 static gint ibus_registry_deserialize (IBusRegistry *registry,
67 static gboolean ibus_registry_copy (IBusRegistry *dest,
68 const IBusRegistry *src);
70 G_DEFINE_TYPE (IBusRegistry, ibus_registry, IBUS_TYPE_SERIALIZABLE)
73 ibus_registry_class_init (IBusRegistryClass *class)
75 GObjectClass *gobject_class = G_OBJECT_CLASS (class);
76 IBusObjectClass *ibus_object_class = IBUS_OBJECT_CLASS (class);
77 IBusSerializableClass *serializable_class = IBUS_SERIALIZABLE_CLASS (class);
79 ibus_object_class->destroy = (IBusObjectDestroyFunc) ibus_registry_destroy;
81 serializable_class->serialize =
82 (IBusSerializableSerializeFunc) ibus_registry_serialize;
83 serializable_class->deserialize =
84 (IBusSerializableDeserializeFunc) ibus_registry_deserialize;
85 serializable_class->copy = (IBusSerializableCopyFunc) ibus_registry_copy;
87 g_type_class_add_private (class, sizeof (IBusRegistryPrivate));
91 * IBusRegistry::changed:
92 * @registry: An #IBusRegistry.
94 * Emitted when any observed paths are changed.
95 * A method is not associated in this class. the "changed"
96 * signal would be handled in other classes.
98 * See also: ibus_registry_start_monitor_changes().
101 g_signal_new (I_("changed"),
102 G_TYPE_FROM_CLASS (gobject_class),
106 _ibus_marshal_VOID__VOID,
112 ibus_registry_init (IBusRegistry *registry)
114 registry->priv = IBUS_REGISTRY_GET_PRIVATE (registry);
116 registry->priv->observed_paths = NULL;
117 registry->priv->components = NULL;
118 registry->priv->changed = FALSE;
119 registry->priv->monitor_table =
120 g_hash_table_new_full (g_file_hash,
121 (GEqualFunc) g_file_equal,
122 (GDestroyNotify) g_object_unref,
123 (GDestroyNotify) g_object_unref);
127 ibus_registry_destroy (IBusRegistry *registry)
129 ibus_registry_remove_all (registry);
131 g_hash_table_destroy (registry->priv->monitor_table);
132 registry->priv->monitor_table = NULL;
134 if (registry->priv->monitor_timeout_id > 0) {
135 g_source_remove (registry->priv->monitor_timeout_id);
136 registry->priv->monitor_timeout_id = 0;
139 IBUS_OBJECT_CLASS (ibus_registry_parent_class)->
140 destroy (IBUS_OBJECT (registry));
144 ibus_registry_serialize (IBusRegistry *registry,
145 GVariantBuilder *builder)
149 retval = IBUS_SERIALIZABLE_CLASS (ibus_registry_parent_class)->
150 serialize ((IBusSerializable *)registry, builder);
151 g_return_val_if_fail (retval, FALSE);
154 GVariantBuilder *array;
156 array = g_variant_builder_new (G_VARIANT_TYPE ("av"));
157 for (p = registry->priv->observed_paths; p != NULL; p = p->next) {
158 IBusSerializable *serializable = (IBusSerializable *) p->data;
159 g_variant_builder_add (array,
161 ibus_serializable_serialize (serializable));
163 g_variant_builder_add (builder, "av", array);
164 g_variant_builder_unref (array);
166 array = g_variant_builder_new (G_VARIANT_TYPE ("av"));
167 for (p = registry->priv->components; p != NULL; p = p->next) {
168 IBusSerializable *serializable = (IBusSerializable *) p->data;
169 g_variant_builder_add (array,
171 ibus_serializable_serialize (serializable));
173 g_variant_builder_add (builder, "av", array);
174 g_variant_builder_unref (array);
180 ibus_registry_deserialize (IBusRegistry *registry,
187 retval = IBUS_SERIALIZABLE_CLASS (ibus_registry_parent_class)->
188 deserialize ((IBusSerializable *)registry, variant);
189 g_return_val_if_fail (retval, 0);
191 g_variant_get_child (variant, retval++, "av", &iter);
192 while (g_variant_iter_loop (iter, "v", &var)) {
193 IBusSerializable *serializable = ibus_serializable_deserialize (var);
194 registry->priv->observed_paths =
195 g_list_append (registry->priv->observed_paths,
196 IBUS_OBSERVED_PATH (serializable));
198 g_variant_iter_free (iter);
200 g_variant_get_child (variant, retval++, "av", &iter);
201 while (g_variant_iter_loop (iter, "v", &var)) {
202 IBusSerializable *serializable = ibus_serializable_deserialize (var);
203 registry->priv->components =
204 g_list_append (registry->priv->components,
205 IBUS_COMPONENT (serializable));
207 g_variant_iter_free (iter);
213 ibus_registry_copy (IBusRegistry *dest,
214 const IBusRegistry *src)
218 retval = IBUS_SERIALIZABLE_CLASS (ibus_registry_parent_class)->
219 copy ((IBusSerializable *)dest, (IBusSerializable *)src);
220 g_return_val_if_fail (retval, FALSE);
222 dest->priv->components = g_list_copy (src->priv->components);
223 dest->priv->observed_paths = g_list_copy (src->priv->observed_paths);
229 * ibus_registry_remove_all:
231 * Remove the loaded registry.
234 ibus_registry_remove_all (IBusRegistry *registry)
236 g_assert (IBUS_IS_REGISTRY (registry));
238 g_list_free_full (registry->priv->observed_paths, g_object_unref);
239 registry->priv->observed_paths = NULL;
241 g_list_free_full (registry->priv->components, g_object_unref);
242 registry->priv->components = NULL;
246 ibus_registry_load (IBusRegistry *registry)
250 gchar **d, **search_path;
252 g_assert (IBUS_IS_REGISTRY (registry));
254 path = g_ptr_array_new();
256 envstr = g_getenv ("IBUS_COMPONENT_PATH");
258 gchar **dirs = g_strsplit (envstr, G_SEARCHPATH_SEPARATOR_S, 0);
259 for (d = dirs; *d != NULL; d++)
260 g_ptr_array_add (path, *d);
265 dirname = g_build_filename (IBUS_DATA_DIR, "component", NULL);
266 g_ptr_array_add (path, dirname);
269 /* FIXME Should we support install some IME in user dir? */
270 dirname = g_build_filename (g_get_user_data_dir (),
273 g_ptr_array_add (path, dirname);
277 g_ptr_array_add (path, NULL);
278 search_path = (gchar **) g_ptr_array_free (path, FALSE);
279 for (d = search_path; *d != NULL; d++) {
280 ibus_registry_load_in_dir (registry, *d);
282 g_strfreev (search_path);
286 ibus_registry_load_cache (IBusRegistry *registry,
292 g_assert (IBUS_IS_REGISTRY (registry));
295 filename = g_build_filename (g_get_user_cache_dir (),
296 "ibus", "bus", "registry", NULL);
298 filename = g_build_filename (IBUS_CACHE_DIR,
299 "bus", "registry", NULL);
302 retval = ibus_registry_load_cache_file (registry, filename);
309 ibus_registry_load_cache_file (IBusRegistry *registry,
310 const gchar *filename)
317 g_assert (IBUS_IS_REGISTRY (registry));
318 g_assert (filename != NULL);
320 if (!g_file_test (filename, G_FILE_TEST_EXISTS))
324 if (!g_file_get_contents (filename, &contents, &length, &error)) {
325 g_warning ("cannot read %s: %s", filename, error->message);
326 g_error_free (error);
332 /* read file header including magic and version */
338 if (GUINT32_FROM_BE (*(guint32 *) p) != IBUS_CACHE_MAGIC) {
344 if (GUINT32_FROM_BE (*(guint32 *) p) != IBUS_CACHE_VERSION) {
350 /* read serialized IBusRegistry */
351 variant = g_variant_new_from_data (G_VARIANT_TYPE ("(sa{sv}avav)"),
353 length - (p - contents),
355 (GDestroyNotify) g_free,
357 if (variant == NULL) {
362 ibus_registry_deserialize (registry, variant);
363 g_variant_unref (variant);
370 ibus_registry_check_modification (IBusRegistry *registry)
374 g_assert (IBUS_IS_REGISTRY (registry));
376 for (p = registry->priv->observed_paths; p != NULL; p = p->next) {
377 if (ibus_observed_path_check_modification (
378 (IBusObservedPath *) p->data))
382 for (p = registry->priv->components; p != NULL; p = p->next) {
383 if (ibus_component_check_modification ((IBusComponent *) p->data))
391 ibus_registry_save_cache (IBusRegistry *registry,
397 g_assert (IBUS_IS_REGISTRY (registry));
400 filename = g_build_filename (g_get_user_cache_dir (),
401 "ibus", "bus", "registry", NULL);
403 filename = g_build_filename (IBUS_CACHE_DIR,
404 "bus", "registry", NULL);
407 retval = ibus_registry_save_cache_file (registry, filename);
414 ibus_registry_save_cache_file (IBusRegistry *registry,
415 const gchar *filename)
425 g_assert (IBUS_IS_REGISTRY (registry));
426 g_assert (filename != NULL);
428 cachedir = g_path_get_dirname (filename);
429 g_mkdir_with_parents (cachedir, 0775);
432 variant = ibus_serializable_serialize (IBUS_SERIALIZABLE (registry));
433 length = 8 + g_variant_get_size (variant);
434 p = contents = g_slice_alloc (length);
436 /* write file header */
437 intval = GUINT32_TO_BE (IBUS_CACHE_MAGIC);
438 memcpy (p, (gchar *) &intval, 4);
441 intval = GUINT32_TO_BE (IBUS_CACHE_VERSION);
442 memcpy (p, (gchar *) &intval, 4);
445 /* write serialized IBusRegistry */
446 g_variant_store (variant, p);
449 retval = g_file_set_contents (filename, contents, length, &error);
451 g_variant_unref (variant);
452 g_slice_free1 (length, contents);
455 g_warning ("cannot write %s: %s", filename, error->message);
456 g_error_free (error);
460 if (g_str_has_prefix (filename, g_get_user_cache_dir ())) {
461 g_chmod (filename, 0644);
467 #define g_string_append_indent(string, indent) \
470 for (i = 0; i < (indent); i++) { \
471 g_string_append (string, " "); \
476 ibus_registry_output (IBusRegistry *registry,
482 g_assert (IBUS_IS_REGISTRY (registry));
483 g_return_if_fail (output != NULL);
485 g_string_append (output, "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n");
486 g_string_append (output, "<ibus-registry>\n");
488 if (registry->priv->observed_paths) {
489 g_string_append_indent (output, indent);
490 g_string_append (output, "<observed-paths>\n");
491 for (p = registry->priv->observed_paths; p != NULL; p = p->next) {
492 ibus_observed_path_output ((IBusObservedPath *) p->data,
495 g_string_append_indent (output, indent);
496 g_string_append (output, "</observed-paths>\n");
499 if (registry->priv->components) {
500 g_string_append_indent (output, indent);
501 g_string_append (output, "<components>\n");
502 for (p = registry->priv->components; p != NULL; p = p->next) {
503 ibus_component_output ((IBusComponent *) p->data,
506 g_string_append_indent (output, indent);
507 g_string_append (output, "</components>\n");
510 g_string_append (output, "</ibus-registry>\n");
514 ibus_registry_load_in_dir (IBusRegistry *registry,
515 const gchar *dirname)
517 GError *error = NULL;
519 IBusObservedPath *observed_path = NULL;
520 const gchar *filename;
522 g_assert (IBUS_IS_REGISTRY (registry));
525 dir = g_dir_open (dirname, 0, &error);
528 g_warning ("Unable open directory %s : %s", dirname, error->message);
529 g_error_free (error);
533 observed_path = ibus_observed_path_new (dirname, TRUE);
535 registry->priv->observed_paths =
536 g_list_append (registry->priv->observed_paths,
539 while ((filename = g_dir_read_name (dir)) != NULL) {
542 IBusComponent *component;
544 size = g_utf8_strlen (filename, -1);
545 if (g_strcmp0 (MAX (filename, filename + size - 4), ".xml") != 0)
548 path = g_build_filename (dirname, filename, NULL);
549 component = ibus_component_new_from_file (path);
550 if (component != NULL) {
551 g_object_ref_sink (component);
552 registry->priv->components =
553 g_list_append (registry->priv->components, component);
564 ibus_registry_new (void)
566 IBusRegistry *registry;
567 registry = (IBusRegistry *) g_object_new (IBUS_TYPE_REGISTRY, NULL);
572 ibus_registry_get_components (IBusRegistry *registry)
574 g_assert (IBUS_IS_REGISTRY (registry));
576 return g_list_copy (registry->priv->components);
580 ibus_registry_get_observed_paths (IBusRegistry *registry)
582 g_assert (IBUS_IS_REGISTRY (registry));
584 return g_list_copy (registry->priv->observed_paths);
588 _monitor_timeout_cb (IBusRegistry *registry)
590 g_hash_table_remove_all (registry->priv->monitor_table);
591 registry->priv->changed = TRUE;
592 g_signal_emit (registry, _signals[CHANGED], 0);
593 registry->priv->monitor_timeout_id = 0;
598 _monitor_changed_cb (GFileMonitor *monitor,
601 GFileMonitorEvent event_type,
602 IBusRegistry *registry)
604 g_assert (IBUS_IS_REGISTRY (registry));
606 if (event_type != G_FILE_MONITOR_EVENT_CHANGED &&
607 event_type != G_FILE_MONITOR_EVENT_DELETED &&
608 event_type != G_FILE_MONITOR_EVENT_CREATED &&
609 event_type != G_FILE_MONITOR_EVENT_ATTRIBUTE_CHANGED)
612 /* Merge successive file changes into one, with a low priority
614 if (registry->priv->monitor_timeout_id > 0)
617 registry->priv->monitor_timeout_id =
618 g_timeout_add_full (G_PRIORITY_DEFAULT_IDLE,
620 (GSourceFunc) _monitor_timeout_cb,
621 g_object_ref (registry),
622 (GDestroyNotify) g_object_unref);
626 ibus_registry_start_monitor_changes (IBusRegistry *registry)
628 GList *observed_paths, *p;
630 g_assert (IBUS_IS_REGISTRY (registry));
632 g_hash_table_remove_all (registry->priv->monitor_table);
634 observed_paths = g_list_copy (registry->priv->observed_paths);
635 for (p = registry->priv->components; p != NULL; p = p->next) {
636 IBusComponent *component = (IBusComponent *) p->data;
637 GList *component_observed_paths =
638 ibus_component_get_observed_paths (component);
639 observed_paths = g_list_concat (observed_paths,
640 component_observed_paths);
643 for (p = observed_paths; p != NULL; p = p->next) {
644 IBusObservedPath *path = (IBusObservedPath *) p->data;
645 GFile *file = g_file_new_for_path (path->path);
646 if (g_hash_table_lookup (registry->priv->monitor_table, file) == NULL) {
647 GFileMonitor *monitor;
651 monitor = g_file_monitor (file,
656 if (monitor != NULL) {
657 g_signal_connect (monitor, "changed",
658 G_CALLBACK (_monitor_changed_cb),
661 g_hash_table_replace (registry->priv->monitor_table,
665 g_warning ("Can't monitor directory %s: %s",
668 g_error_free (error);
671 g_object_unref (file);
673 g_list_free (observed_paths);