Clean spec file for yocto compliance.
[platform/upstream/ibus.git] / src / ibusregistry.c
1 /* -*- mode: C; c-basic-offset: 4; indent-tabs-mode: nil; -*- */
2 /* vim:set et sts=4: */
3 /* bus - The Input Bus
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.
7  *
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.1 of the License, or (at your option) any later version.
12  *
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.
17  *
18  * You should have received a copy of the GNU Lesser General Public
19  * License along with this library; if not, write to the Free Software
20  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301
21  * USA
22  */
23 #include <gio/gio.h>
24 #include <glib/gstdio.h>
25 #include <string.h>
26
27 #include "ibusinternal.h"
28 #include "ibusmarshalers.h"
29 #include "ibusregistry.h"
30
31 #define IBUS_CACHE_MAGIC 0x49425553 /* "IBUS" */
32 #define IBUS_CACHE_VERSION 0x00010502
33
34 enum {
35     CHANGED,
36     LAST_SIGNAL,
37 };
38
39 static guint             _signals[LAST_SIGNAL] = { 0 };
40
41 struct _IBusRegistryPrivate {
42     /* a list of IBusObservedPath objects. */
43     GList *observed_paths;
44
45     /* a list of IBusComponent objects that are created from component XML
46      * files (or from the cache of them). */
47     GList *components;
48
49     gboolean changed;
50
51     /* a mapping from GFile to GFileMonitor. */
52     GHashTable *monitor_table;
53
54     guint monitor_timeout_id;
55 };
56
57 #define IBUS_REGISTRY_GET_PRIVATE(o)  \
58    (G_TYPE_INSTANCE_GET_PRIVATE ((o), IBUS_TYPE_REGISTRY, IBusRegistryPrivate))
59
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,
66                                               GVariant               *variant);
67 static gboolean ibus_registry_copy           (IBusRegistry           *dest,
68                                               const IBusRegistry     *src);
69
70 G_DEFINE_TYPE (IBusRegistry, ibus_registry, IBUS_TYPE_SERIALIZABLE)
71
72 static void
73 ibus_registry_class_init (IBusRegistryClass *class)
74 {
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);
78
79     ibus_object_class->destroy = (IBusObjectDestroyFunc) ibus_registry_destroy;
80
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;
86
87     g_type_class_add_private (class, sizeof (IBusRegistryPrivate));
88
89     /* install signals */
90     /**
91      * IBusRegistry::changed:
92      * @registry: An #IBusRegistry.
93      *
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.
97      *
98      * See also: ibus_registry_start_monitor_changes().
99      */
100     _signals[CHANGED] =
101         g_signal_new (I_("changed"),
102             G_TYPE_FROM_CLASS (gobject_class),
103             G_SIGNAL_RUN_LAST,
104             0,
105             NULL, NULL,
106             _ibus_marshal_VOID__VOID,
107             G_TYPE_NONE,
108             0);
109 }
110
111 static void
112 ibus_registry_init (IBusRegistry *registry)
113 {
114     registry->priv = IBUS_REGISTRY_GET_PRIVATE (registry);
115
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);
124 }
125
126 static void
127 ibus_registry_destroy (IBusRegistry *registry)
128 {
129     ibus_registry_remove_all (registry);
130
131     g_hash_table_destroy (registry->priv->monitor_table);
132     registry->priv->monitor_table = NULL;
133
134     if (registry->priv->monitor_timeout_id > 0) {
135         g_source_remove (registry->priv->monitor_timeout_id);
136         registry->priv->monitor_timeout_id = 0;
137     }
138
139     IBUS_OBJECT_CLASS (ibus_registry_parent_class)->
140             destroy (IBUS_OBJECT (registry));
141 }
142
143 static gboolean
144 ibus_registry_serialize (IBusRegistry    *registry,
145                          GVariantBuilder *builder)
146 {
147     gboolean retval;
148
149     retval = IBUS_SERIALIZABLE_CLASS (ibus_registry_parent_class)->
150         serialize ((IBusSerializable *)registry, builder);
151     g_return_val_if_fail (retval, FALSE);
152
153     GList *p;
154     GVariantBuilder *array;
155
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,
160                                "v",
161                                ibus_serializable_serialize (serializable));
162     }
163     g_variant_builder_add (builder, "av", array);
164     g_variant_builder_unref (array);
165
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,
170                                "v",
171                                ibus_serializable_serialize (serializable));
172     }
173     g_variant_builder_add (builder, "av", array);
174     g_variant_builder_unref (array);
175
176     return TRUE;
177 }
178
179 static gint
180 ibus_registry_deserialize (IBusRegistry *registry,
181                            GVariant     *variant)
182 {
183     GVariant *var;
184     GVariantIter *iter;
185     gint retval;
186
187     retval = IBUS_SERIALIZABLE_CLASS (ibus_registry_parent_class)->
188         deserialize ((IBusSerializable *)registry, variant);
189     g_return_val_if_fail (retval, 0);
190
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));
197     }
198     g_variant_iter_free (iter);
199
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));
206     }
207     g_variant_iter_free (iter);
208
209     return retval;
210 }
211
212 static gboolean
213 ibus_registry_copy (IBusRegistry       *dest,
214                     const IBusRegistry *src)
215 {
216     gboolean retval;
217
218     retval = IBUS_SERIALIZABLE_CLASS (ibus_registry_parent_class)->
219         copy ((IBusSerializable *)dest, (IBusSerializable *)src);
220     g_return_val_if_fail (retval, FALSE);
221
222     dest->priv->components = g_list_copy (src->priv->components);
223     dest->priv->observed_paths = g_list_copy (src->priv->observed_paths);
224
225     return TRUE;
226 }
227
228 /**
229  * ibus_registry_remove_all:
230  *
231  * Remove the loaded registry.
232  */
233 static void
234 ibus_registry_remove_all (IBusRegistry *registry)
235 {
236     g_assert (IBUS_IS_REGISTRY (registry));
237
238     g_list_free_full (registry->priv->observed_paths, g_object_unref);
239     registry->priv->observed_paths = NULL;
240
241     g_list_free_full (registry->priv->components, g_object_unref);
242     registry->priv->components = NULL;
243 }
244
245 void
246 ibus_registry_load (IBusRegistry *registry)
247 {
248     const gchar *envstr;
249     GPtrArray *path;
250     gchar **d, **search_path;
251
252     g_assert (IBUS_IS_REGISTRY (registry));
253
254     path = g_ptr_array_new();
255
256     envstr = g_getenv ("IBUS_COMPONENT_PATH");
257     if (envstr) {
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);
261         g_free (dirs);
262     } else {
263         gchar *dirname;
264
265         dirname = g_build_filename (IBUS_DATA_DIR, "component", NULL);
266         g_ptr_array_add (path, dirname);
267
268 #if 0
269         /* FIXME Should we support install some IME in user dir? */
270         dirname = g_build_filename (g_get_user_data_dir (),
271                                     "ibus", "component",
272                                     NULL);
273         g_ptr_array_add (path, dirname);
274 #endif
275     }
276
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);
281     }
282     g_strfreev (search_path);
283 }
284
285 gboolean
286 ibus_registry_load_cache (IBusRegistry *registry,
287                           gboolean      is_user)
288 {
289     gchar *filename;
290     gboolean retval;
291
292     g_assert (IBUS_IS_REGISTRY (registry));
293
294     if (is_user) {
295         filename = g_build_filename (g_get_user_cache_dir (),
296                                      "ibus", "bus", "registry", NULL);
297     } else {
298         filename = g_build_filename (IBUS_CACHE_DIR,
299                                      "bus", "registry", NULL);
300     }
301
302     retval = ibus_registry_load_cache_file (registry, filename);
303     g_free (filename);
304
305     return retval;
306 }
307
308 gboolean
309 ibus_registry_load_cache_file (IBusRegistry *registry,
310                                const gchar  *filename)
311 {
312     gchar *contents, *p;
313     gsize length;
314     GVariant *variant;
315     GError *error;
316
317     g_assert (IBUS_IS_REGISTRY (registry));
318     g_assert (filename != NULL);
319
320     if (!g_file_test (filename, G_FILE_TEST_EXISTS))
321         return FALSE;
322
323     error = NULL;
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);
327         return FALSE;
328     }
329
330     p = contents;
331
332     /* read file header including magic and version */
333     if (length < 8) {
334         g_free (contents);
335         return FALSE;
336     }
337
338     if (GUINT32_FROM_BE (*(guint32 *) p) != IBUS_CACHE_MAGIC) {
339         g_free (contents);
340         return FALSE;
341     }
342     p += 4;
343
344     if (GUINT32_FROM_BE (*(guint32 *) p) != IBUS_CACHE_VERSION) {
345         g_free (contents);
346         return FALSE;
347     }
348     p += 4;
349
350     /* read serialized IBusRegistry */
351     variant = g_variant_new_from_data (G_VARIANT_TYPE ("(sa{sv}avav)"),
352                                        p,
353                                        length - (p - contents),
354                                        FALSE,
355                                        (GDestroyNotify) g_free,
356                                        NULL);
357     if (variant == NULL) {
358         g_free (contents);
359         return FALSE;
360     }
361
362     ibus_registry_deserialize (registry, variant);
363     g_variant_unref (variant);
364     g_free (contents);
365
366     return TRUE;
367 }
368
369 gboolean
370 ibus_registry_check_modification (IBusRegistry *registry)
371 {
372     GList *p;
373
374     g_assert (IBUS_IS_REGISTRY (registry));
375
376     for (p = registry->priv->observed_paths; p != NULL; p = p->next) {
377         if (ibus_observed_path_check_modification (
378                     (IBusObservedPath *) p->data))
379             return TRUE;
380     }
381
382     for (p = registry->priv->components; p != NULL; p = p->next) {
383         if (ibus_component_check_modification ((IBusComponent *) p->data))
384             return TRUE;
385     }
386
387     return FALSE;
388 }
389
390 gboolean
391 ibus_registry_save_cache (IBusRegistry *registry,
392                           gboolean      is_user)
393 {
394     gchar *filename;
395     gboolean retval;
396
397     g_assert (IBUS_IS_REGISTRY (registry));
398
399     if (is_user) {
400         filename = g_build_filename (g_get_user_cache_dir (),
401                                      "ibus", "bus", "registry", NULL);
402     } else {
403         filename = g_build_filename (IBUS_CACHE_DIR,
404                                      "bus", "registry", NULL);
405     }
406
407     retval = ibus_registry_save_cache_file (registry, filename);
408     g_free (filename);
409
410     return retval;
411 }
412
413 gboolean
414 ibus_registry_save_cache_file (IBusRegistry *registry,
415                                const gchar  *filename)
416 {
417     gchar *cachedir;
418     GVariant *variant;
419     gchar *contents, *p;
420     gsize length;
421     gboolean retval;
422     guint32 intval;
423     GError *error;
424
425     g_assert (IBUS_IS_REGISTRY (registry));
426     g_assert (filename != NULL);
427
428     cachedir = g_path_get_dirname (filename);
429     g_mkdir_with_parents (cachedir, 0775);
430     g_free (cachedir);
431
432     variant = ibus_serializable_serialize (IBUS_SERIALIZABLE (registry));
433     length = 8 + g_variant_get_size (variant);
434     p = contents = g_slice_alloc (length);
435
436     /* write file header */
437     intval = GUINT32_TO_BE (IBUS_CACHE_MAGIC);
438     memcpy (p, (gchar *) &intval, 4);
439     p += 4;
440
441     intval = GUINT32_TO_BE (IBUS_CACHE_VERSION);
442     memcpy (p, (gchar *) &intval, 4);
443     p += 4;
444
445     /* write serialized IBusRegistry */
446     g_variant_store (variant, p);
447
448     error = NULL;
449     retval = g_file_set_contents (filename, contents, length, &error);
450
451     g_variant_unref (variant);
452     g_slice_free1 (length, contents);
453
454     if (!retval) {
455         g_warning ("cannot write %s: %s", filename, error->message);
456         g_error_free (error);
457         return FALSE;
458     }
459
460     if (g_str_has_prefix (filename, g_get_user_cache_dir ())) {
461         g_chmod (filename, 0644);
462     }
463
464     return TRUE;
465 }
466
467 #define g_string_append_indent(string, indent)  \
468     {                                           \
469         gint i;                                 \
470         for (i = 0; i < (indent); i++) {        \
471             g_string_append (string, "    ");   \
472         }                                       \
473     }
474
475 void
476 ibus_registry_output (IBusRegistry *registry,
477                       GString      *output,
478                       int           indent)
479 {
480     GList *p;
481
482     g_assert (IBUS_IS_REGISTRY (registry));
483     g_return_if_fail (output != NULL);
484
485     g_string_append (output, "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n");
486     g_string_append (output, "<ibus-registry>\n");
487
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,
493                                       output, indent * 2);
494         }
495         g_string_append_indent (output, indent);
496         g_string_append (output, "</observed-paths>\n");
497     }
498
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,
504                                    output, indent * 2);
505         }
506         g_string_append_indent (output, indent);
507         g_string_append (output, "</components>\n");
508     }
509
510     g_string_append (output, "</ibus-registry>\n");
511 }
512
513 void
514 ibus_registry_load_in_dir (IBusRegistry *registry,
515                            const gchar  *dirname)
516 {
517     GError *error = NULL;
518     GDir *dir;
519     IBusObservedPath *observed_path = NULL;
520     const gchar *filename;
521
522     g_assert (IBUS_IS_REGISTRY (registry));
523     g_assert (dirname);
524
525     dir = g_dir_open (dirname, 0, &error);
526
527     if (dir == NULL) {
528         g_warning ("Unable open directory %s : %s", dirname, error->message);
529         g_error_free (error);
530         return;
531     }
532
533     observed_path = ibus_observed_path_new (dirname, TRUE);
534
535     registry->priv->observed_paths =
536             g_list_append (registry->priv->observed_paths,
537                            observed_path);
538
539     while ((filename = g_dir_read_name (dir)) != NULL) {
540         glong size;
541         gchar *path;
542         IBusComponent *component;
543
544         size = g_utf8_strlen (filename, -1);
545         if (g_strcmp0 (MAX (filename, filename + size - 4), ".xml") != 0)
546             continue;
547
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);
554         }
555
556         g_free (path);
557     }
558
559     g_dir_close (dir);
560 }
561
562
563 IBusRegistry *
564 ibus_registry_new (void)
565 {
566     IBusRegistry *registry;
567     registry = (IBusRegistry *) g_object_new (IBUS_TYPE_REGISTRY, NULL);
568     return registry;
569 }
570
571 GList *
572 ibus_registry_get_components (IBusRegistry *registry)
573 {
574     g_assert (IBUS_IS_REGISTRY (registry));
575
576     return g_list_copy (registry->priv->components);
577 }
578
579 GList *
580 ibus_registry_get_observed_paths (IBusRegistry *registry)
581 {
582     g_assert (IBUS_IS_REGISTRY (registry));
583
584     return g_list_copy (registry->priv->observed_paths);
585 }
586
587 static gboolean
588 _monitor_timeout_cb (IBusRegistry *registry)
589 {
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;
594     return FALSE;
595 }
596
597 static void
598 _monitor_changed_cb (GFileMonitor     *monitor,
599                      GFile            *file,
600                      GFile            *other_file,
601                      GFileMonitorEvent event_type,
602                      IBusRegistry     *registry)
603 {
604     g_assert (IBUS_IS_REGISTRY (registry));
605
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)
610         return;
611
612     /* Merge successive file changes into one, with a low priority
613        timeout handler. */
614     if (registry->priv->monitor_timeout_id > 0)
615         return;
616
617     registry->priv->monitor_timeout_id =
618         g_timeout_add_full (G_PRIORITY_DEFAULT_IDLE,
619                             5000,
620                             (GSourceFunc) _monitor_timeout_cb,
621                             g_object_ref (registry),
622                             (GDestroyNotify) g_object_unref);
623 }
624
625 void
626 ibus_registry_start_monitor_changes (IBusRegistry *registry)
627 {
628     GList *observed_paths, *p;
629
630     g_assert (IBUS_IS_REGISTRY (registry));
631
632     g_hash_table_remove_all (registry->priv->monitor_table);
633
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);
641     }
642
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;
648             GError *error;
649
650             error = NULL;
651             monitor = g_file_monitor (file,
652                                       G_FILE_MONITOR_NONE,
653                                       NULL,
654                                       &error);
655
656             if (monitor != NULL) {
657                 g_signal_connect (monitor, "changed",
658                                   G_CALLBACK (_monitor_changed_cb),
659                                   registry);
660
661                 g_hash_table_replace (registry->priv->monitor_table,
662                                       g_object_ref (file),
663                                       monitor);
664             } else {
665                 g_warning ("Can't monitor directory %s: %s",
666                            path->path,
667                            error->message);
668                 g_error_free (error);
669             }
670         }
671         g_object_unref (file);
672     }
673     g_list_free (observed_paths);
674 }