ibus-daemon: use GFileMonitor instead of polling
[platform/upstream/ibus.git] / bus / registry.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) 2008-2010 Peng Huang <shawn.p.huang@gmail.com>
5  * Copyright (C) 2008-2010 Red Hat, Inc.
6  *
7  * This library is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU Lesser General Public
9  * License as published by the Free Software Foundation; either
10  * version 2 of the License, or (at your option) any later version.
11  *
12  * This library is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  * Lesser General Public License for more details.
16  *
17  * You should have received a copy of the GNU Lesser General Public
18  * License along with this library; if not, write to the
19  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
20  * Boston, MA 02111-1307, USA.
21  */
22 #include "registry.h"
23
24 #include <gio/gio.h>
25 #include <glib/gstdio.h>
26 #include <stdlib.h>
27 #include <string.h>
28
29 #include "dbusimpl.h"
30 #include "global.h"
31 #include "marshalers.h"
32 #include "types.h"
33
34 enum {
35     CHANGED,
36     LAST_SIGNAL,
37 };
38
39 static guint             _signals[LAST_SIGNAL] = { 0 };
40
41 struct _BusRegistry {
42     IBusObject parent;
43
44     /* instance members */
45
46     /* a list of IBusObservedPath objects. */
47     GList *observed_paths;
48     /* a list of BusComponent objects that are created from component XML files (or from the cache of them). */
49     GList *components;
50     /* a mapping from an engine name (e.g. 'pinyin') to the corresponding IBusEngineDesc object. */
51     GHashTable *engine_table;
52     gboolean changed;
53     /* a mapping from GFile to GFileMonitor. */
54     GHashTable *monitor_table;
55     guint monitor_timeout_id;
56 };
57
58 struct _BusRegistryClass {
59     IBusObjectClass parent;
60
61     /* class members */
62 };
63
64 /* functions prototype */
65 static void              bus_registry_destroy           (BusRegistry        *registry);
66 static void              bus_registry_load              (BusRegistry        *registry);
67 static void              bus_registry_load_in_dir       (BusRegistry        *registry,
68                                                          const gchar        *dirname);
69 static gboolean          bus_registry_save_cache        (BusRegistry        *registry);
70 static gboolean          bus_registry_load_cache        (BusRegistry        *registry);
71 static gboolean          bus_registry_check_modification(BusRegistry        *registry);
72 static void              bus_registry_remove_all        (BusRegistry        *registry);
73
74 G_DEFINE_TYPE (BusRegistry, bus_registry, IBUS_TYPE_OBJECT)
75
76 static void
77 bus_registry_class_init (BusRegistryClass *class)
78 {
79     GObjectClass *gobject_class = G_OBJECT_CLASS (class);
80     IBusObjectClass *ibus_object_class = IBUS_OBJECT_CLASS (class);
81
82     _signals[CHANGED] =
83         g_signal_new (I_("changed"),
84             G_TYPE_FROM_CLASS (gobject_class),
85             G_SIGNAL_RUN_LAST,
86             0, /* does not associate a method in this class. the "changed" signal would be handled in other classes. */
87             NULL, NULL,
88             bus_marshal_VOID__VOID,
89             G_TYPE_NONE,
90             0);
91
92     ibus_object_class->destroy = (IBusObjectDestroyFunc) bus_registry_destroy;
93 }
94
95 static void
96 bus_registry_init (BusRegistry *registry)
97 {
98     GList *p;
99     registry->observed_paths = NULL;
100     registry->components = NULL;
101     registry->engine_table = g_hash_table_new (g_str_hash, g_str_equal);
102     registry->changed = FALSE;
103     registry->monitor_table =
104         g_hash_table_new_full (g_file_hash,
105                                (GEqualFunc) g_file_equal,
106                                (GDestroyNotify) g_object_unref,
107                                (GDestroyNotify) g_object_unref);
108
109     if (g_strcmp0 (g_cache, "none") == 0) {
110         /* Only load registry, but not read and write cache. */
111         bus_registry_load (registry);
112     }
113     else if (g_strcmp0 (g_cache, "refresh") == 0) {
114         /* Load registry and overwrite the cache. */
115         bus_registry_load (registry);
116         bus_registry_save_cache (registry);
117     }
118     else {
119         /* Load registry from cache. If the cache does not exist or
120          * it is outdated, then generate it.
121          */
122         if (bus_registry_load_cache (registry) == FALSE ||
123             bus_registry_check_modification (registry)) {
124             bus_registry_remove_all (registry);
125             bus_registry_load (registry);
126             bus_registry_save_cache (registry);
127         }
128     }
129
130     for (p = registry->components; p != NULL; p = p->next) {
131         BusComponent *comp = (BusComponent *) p->data;
132         GList *engines = bus_component_get_engines (comp);
133         GList *p1;
134         for (p1 = engines; p1 != NULL; p1 = p1->next) {
135             IBusEngineDesc *desc = (IBusEngineDesc *) p1->data;
136             const gchar *name = ibus_engine_desc_get_name (desc);
137             if (g_hash_table_lookup (registry->engine_table, name) == NULL) {
138                 g_hash_table_insert (registry->engine_table,
139                                      (gpointer) name,
140                                      desc);
141             } else {
142                 g_message ("Engine %s is already registered by other component",
143                            name);
144             }
145         }
146         g_list_free (engines);
147     }
148 }
149
150 static void
151 bus_registry_remove_all (BusRegistry *registry)
152 {
153     g_list_free_full (registry->observed_paths, g_object_unref);
154     registry->observed_paths = NULL;
155
156     g_list_free_full (registry->components, g_object_unref);
157     registry->components = NULL;
158
159     g_hash_table_remove_all (registry->engine_table);
160     g_hash_table_remove_all (registry->monitor_table);
161 }
162
163 static void
164 bus_registry_destroy (BusRegistry *registry)
165 {
166     bus_registry_remove_all (registry);
167
168     g_hash_table_destroy (registry->engine_table);
169     registry->engine_table = NULL;
170
171     g_hash_table_destroy (registry->monitor_table);
172     registry->monitor_table = NULL;
173
174     if (registry->monitor_timeout_id > 0) {
175         g_source_remove (registry->monitor_timeout_id);
176         registry->monitor_timeout_id = 0;
177     }
178
179     IBUS_OBJECT_CLASS (bus_registry_parent_class)->destroy (IBUS_OBJECT (registry));
180 }
181
182 /**
183  * bus_registry_load:
184  *
185  * Read all XML files in the PKGDATADIR (typically /usr/share/ibus/components/ *.xml) and update the registry object.
186  */
187 static void
188 bus_registry_load (BusRegistry *registry)
189 {
190     g_assert (BUS_IS_REGISTRY (registry));
191
192     const gchar *envstr;
193     GPtrArray *path;
194     gchar **d, **search_path;
195
196     path = g_ptr_array_new();
197
198     envstr = g_getenv ("IBUS_COMPONENT_PATH");
199     if (envstr) {
200         gchar **dirs = g_strsplit (envstr, G_SEARCHPATH_SEPARATOR_S, 0);
201         for (d = dirs; *d != NULL; d++)
202             g_ptr_array_add (path, *d);
203         g_free (dirs);
204     } else {
205         gchar *dirname;
206
207         dirname = g_build_filename (PKGDATADIR, "component", NULL);
208         g_ptr_array_add (path, dirname);
209
210 #if 0
211         /* FIXME Should we support install some IME in user dir? */
212         dirname = g_build_filename (g_get_user_data_dir (),
213                                     "ibus", "component",
214                                     NULL);
215         g_ptr_array_add (path, dirname);
216 #endif
217     }
218
219     g_ptr_array_add (path, NULL);
220     search_path = (gchar **) g_ptr_array_free (path, FALSE);
221     for (d = search_path; *d != NULL; d++) {
222         IBusObservedPath *observed_path = ibus_observed_path_new (*d, TRUE);
223
224         registry->observed_paths = g_list_append (registry->observed_paths,
225                                                   observed_path);
226
227         bus_registry_load_in_dir (registry, *d);
228     }
229     g_strfreev (search_path);
230 }
231
232 #define g_string_append_indent(string, indent)  \
233     {                                           \
234         gint i;                                 \
235         for (i = 0; i < (indent); i++) {        \
236             g_string_append (string, "    ");   \
237         }                                       \
238     }
239
240 static gboolean
241 bus_registry_load_cache (BusRegistry *registry)
242 {
243     g_assert (BUS_IS_REGISTRY (registry));
244
245     gchar *filename;
246     XMLNode *node;
247     GList *p;
248
249     filename = g_build_filename (g_get_user_cache_dir (), "ibus", "bus", "registry.xml", NULL);
250     node = ibus_xml_parse_file (filename);
251     g_free (filename);
252
253     if (node == NULL) {
254         return FALSE;
255     }
256
257     if (g_strcmp0 (node->name, "ibus-registry") != 0) {
258         ibus_xml_free (node);
259         return FALSE;
260     }
261
262     for (p = node->sub_nodes; p != NULL; p = p->next) {
263         XMLNode *sub_node = (XMLNode *) p->data;
264
265         if (g_strcmp0 (sub_node->name, "observed-paths") == 0) {
266             GList *pp;
267             for (pp = sub_node->sub_nodes; pp != NULL; pp = pp->next) {
268                 IBusObservedPath *path;
269                 path = ibus_observed_path_new_from_xml_node (pp->data, FALSE);
270                 if (path) {
271                     g_object_ref_sink (path);
272                     registry->observed_paths = g_list_append (registry->observed_paths, path);
273                 }
274             }
275             continue;
276         }
277         if (g_strcmp0 (sub_node->name, "components") == 0) {
278             GList *pp;
279             for (pp = sub_node->sub_nodes; pp != NULL; pp = pp->next) {
280                 IBusComponent *component;
281                 component = ibus_component_new_from_xml_node (pp->data);
282                 if (component) {
283                     BusComponent *buscomp = bus_component_new (component,
284                                                                NULL /* factory */);
285                     g_object_ref_sink (buscomp);
286                     registry->components =
287                         g_list_append (registry->components, buscomp);
288                 }
289             }
290
291             continue;
292         }
293         g_warning ("Unknown element <%s>", sub_node->name);
294     }
295
296     ibus_xml_free (node);
297     return TRUE;
298 }
299
300 static gboolean
301 bus_registry_check_modification (BusRegistry *registry)
302 {
303     GList *p;
304
305     for (p = registry->observed_paths; p != NULL; p = p->next) {
306         if (ibus_observed_path_check_modification ((IBusObservedPath *) p->data))
307             return TRUE;
308     }
309
310     for (p = registry->components; p != NULL; p = p->next) {
311         if (ibus_component_check_modification (bus_component_get_component ((BusComponent *) p->data)))
312             return TRUE;
313     }
314
315     return FALSE;
316 }
317
318 static gboolean
319 bus_registry_save_cache (BusRegistry *registry)
320 {
321     g_assert (BUS_IS_REGISTRY (registry));
322
323     gchar *cachedir;
324     gchar *filename;
325     GString *output;
326     GList *p;
327     FILE *pf;
328     size_t items = 0;
329
330     cachedir = g_build_filename (g_get_user_cache_dir (), "ibus", "bus", NULL);
331     filename = g_build_filename (cachedir, "registry.xml", NULL);
332     g_mkdir_with_parents (cachedir, 0775);
333     pf = g_fopen (filename, "w");
334     g_free (filename);
335     g_free (cachedir);
336
337     if (pf == NULL) {
338         g_warning ("create registry.xml failed");
339         return FALSE;
340     }
341
342     output = g_string_new ("");
343     g_string_append (output, "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n");
344     g_string_append (output, "<!-- \n"
345                              "    This file was generated by ibus-daemon. Please don't modify it.\n"
346                              "    -->\n");
347     g_string_append (output, "<ibus-registry>\n");
348
349     if (registry->observed_paths) {
350         g_string_append_indent (output, 1);
351         g_string_append (output, "<observed-paths>\n");
352         for (p = registry->observed_paths; p != NULL; p = p->next) {
353             ibus_observed_path_output ((IBusObservedPath *) p->data,
354                                       output, 2);
355         }
356         g_string_append_indent (output, 1);
357         g_string_append (output, "</observed-paths>\n");
358     }
359
360     if (registry->components) {
361         g_string_append_indent (output, 1);
362         g_string_append (output, "<components>\n");
363         for (p = registry->components; p != NULL; p = p->next) {
364             ibus_component_output (bus_component_get_component ((BusComponent *) p->data),
365                                    output, 2);
366         }
367         g_string_append_indent (output, 1);
368         g_string_append (output, "</components>\n");
369     }
370
371     g_string_append (output, "</ibus-registry>\n");
372     items = fwrite (output->str, output->len, 1, pf);
373     g_string_free (output, TRUE);
374     fclose (pf);
375     return (items == 1 ? TRUE : FALSE);
376 }
377
378 /**
379  * bus_registry_load_in_dir:
380  *
381  * Read all XML files in dirname, create a BusComponent object for each file, and add the component objects to the registry.
382  */
383 static void
384 bus_registry_load_in_dir (BusRegistry *registry,
385                           const gchar *dirname)
386 {
387     g_assert (BUS_IS_REGISTRY (registry));
388     g_assert (dirname);
389
390     GError *error = NULL;
391     GDir *dir;
392     const gchar *filename;
393
394     dir = g_dir_open (dirname, 0, &error);
395
396     if (dir == NULL) {
397         g_warning ("Unable open directory %s : %s", dirname, error->message);
398         g_error_free (error);
399         return;
400     }
401
402     while ((filename = g_dir_read_name (dir)) != NULL) {
403         glong size;
404         gchar *path;
405         IBusComponent *component;
406
407         size = g_utf8_strlen (filename, -1);
408         if (g_strcmp0 (MAX (filename, filename + size - 4), ".xml") != 0)
409             continue;
410
411         path = g_build_filename (dirname, filename, NULL);
412         component = ibus_component_new_from_file (path);
413         if (component != NULL) {
414             BusComponent *buscomp = bus_component_new (component,
415                                                        NULL /* factory */);
416             g_object_ref_sink (buscomp);
417             registry->components =
418                 g_list_append (registry->components, buscomp);
419         }
420
421         g_free (path);
422     }
423
424     g_dir_close (dir);
425 }
426
427
428 BusRegistry *
429 bus_registry_new (void)
430 {
431     BusRegistry *registry;
432     registry = (BusRegistry *) g_object_new (BUS_TYPE_REGISTRY, NULL);
433     return registry;
434 }
435
436 static gint
437 bus_register_component_is_name_cb (BusComponent *component,
438                                    const gchar  *name)
439 {
440     g_assert (BUS_IS_COMPONENT (component));
441     g_assert (name);
442
443     return g_strcmp0 (bus_component_get_name (component), name);
444 }
445
446 BusComponent *
447 bus_registry_lookup_component_by_name (BusRegistry *registry,
448                                        const gchar *name)
449 {
450     g_assert (BUS_IS_REGISTRY (registry));
451     g_assert (name);
452
453     GList *p;
454     p = g_list_find_custom (registry->components,
455                             name,
456                             (GCompareFunc) bus_register_component_is_name_cb);
457     if (p) {
458         return (BusComponent *) p->data;
459     }
460     else {
461         return NULL;
462     }
463 }
464
465 GList *
466 bus_registry_get_components (BusRegistry *registry)
467 {
468     g_assert (BUS_IS_REGISTRY (registry));
469
470     return g_list_copy (registry->components);
471 }
472
473 GList *
474 bus_registry_get_engines (BusRegistry *registry)
475 {
476     g_assert (BUS_IS_REGISTRY (registry));
477
478     return g_hash_table_get_values (registry->engine_table);
479 }
480
481 GList *
482 bus_registry_get_engines_by_language (BusRegistry *registry,
483                                       const gchar *language)
484 {
485     g_assert (BUS_IS_REGISTRY (registry));
486     g_assert (language);
487
488     gint n;
489     GList *p1, *p2, *engines;
490
491     n = strlen (language);
492
493     p1 = bus_registry_get_engines (registry);
494
495     engines = NULL;
496
497     for (p2 = p1; p2 != NULL; p2 = p2->next) {
498         IBusEngineDesc *desc = (IBusEngineDesc *) p2->data;
499         if (strncmp (ibus_engine_desc_get_language (desc), language, n) == 0) {
500             engines = g_list_append (engines, desc);
501         }
502     }
503
504     g_list_free (p1);
505     return engines;
506 }
507
508 IBusEngineDesc *
509 bus_registry_find_engine_by_name (BusRegistry *registry,
510                                   const gchar *name)
511 {
512     g_assert (BUS_IS_REGISTRY (registry));
513     g_assert (name);
514
515     return (IBusEngineDesc *) g_hash_table_lookup (registry->engine_table, name);
516 }
517
518 void
519 bus_registry_stop_all_components (BusRegistry *registry)
520 {
521     g_assert (BUS_IS_REGISTRY (registry));
522
523     g_list_foreach (registry->components, (GFunc) bus_component_stop, NULL);
524
525 }
526
527 static gboolean
528 _monitor_timeout_cb (BusRegistry *registry)
529 {
530     g_hash_table_remove_all (registry->monitor_table);
531     registry->changed = TRUE;
532     g_signal_emit (registry, _signals[CHANGED], 0);
533     registry->monitor_timeout_id = 0;
534     return FALSE;
535 }
536
537 static void
538 _monitor_changed_cb (GFileMonitor     *monitor,
539                      GFile            *file,
540                      GFile            *other_file,
541                      GFileMonitorEvent event_type,
542                      BusRegistry      *registry)
543 {
544     g_assert (BUS_IS_REGISTRY (registry));
545
546     if (event_type != G_FILE_MONITOR_EVENT_CHANGED &&
547         event_type != G_FILE_MONITOR_EVENT_DELETED &&
548         event_type != G_FILE_MONITOR_EVENT_CREATED &&
549         event_type != G_FILE_MONITOR_EVENT_ATTRIBUTE_CHANGED)
550         return;
551
552     /* Merge successive file changes into one, with a low priority
553        timeout handler. */
554     if (registry->monitor_timeout_id > 0)
555         return;
556
557     registry->monitor_timeout_id =
558         g_timeout_add_full (G_PRIORITY_DEFAULT_IDLE,
559                             5000,
560                             (GSourceFunc) _monitor_timeout_cb,
561                             g_object_ref (registry),
562                             (GDestroyNotify) g_object_unref);
563 }
564
565 /**
566  * bus_registry_start_monitor_changes:
567  *
568  * Start the monitor thread.
569  */
570 void
571 bus_registry_start_monitor_changes (BusRegistry *registry)
572 {
573     GList *observed_paths, *p;
574
575     g_assert (BUS_IS_REGISTRY (registry));
576
577     g_hash_table_remove_all (registry->monitor_table);
578
579     observed_paths = g_list_copy (registry->observed_paths);
580     for (p = registry->components; p != NULL; p = p->next) {
581         BusComponent *buscomp = (BusComponent *) p->data;
582         IBusComponent *component = bus_component_get_component (buscomp);
583         GList *component_observed_paths =
584             ibus_component_get_observed_paths (component);
585         observed_paths = g_list_concat (observed_paths,
586                                         component_observed_paths);
587     }
588
589     for (p = observed_paths; p != NULL; p = p->next) {
590         IBusObservedPath *path = (IBusObservedPath *) p->data;
591         GFile *file = g_file_new_for_path (path->path);
592         if (g_hash_table_lookup (registry->monitor_table,file) == NULL) {
593             GFileMonitor *monitor;
594             GError *error;
595
596             error = NULL;
597             monitor = g_file_monitor (file,
598                                       G_FILE_MONITOR_NONE,
599                                       NULL,
600                                       &error);
601
602             if (monitor != NULL) {
603                 g_signal_connect (monitor, "changed",
604                                   G_CALLBACK (_monitor_changed_cb),
605                                   registry);
606
607                 g_hash_table_replace (registry->monitor_table,
608                                       g_object_ref (file),
609                                       monitor);
610             } else {
611                 g_warning ("Can't monitor directory %s: %s",
612                            path->path,
613                            error->message);
614                 g_error_free (error);
615             }
616         }
617         g_object_unref (file);
618     }
619     g_list_free (observed_paths);
620 }
621
622 gboolean
623 bus_registry_is_changed (BusRegistry *registry)
624 {
625     g_assert (BUS_IS_REGISTRY (registry));
626     return (registry->changed != 0);
627 }
628
629 void
630 bus_registry_name_owner_changed (BusRegistry *registry,
631                                  const gchar *name,
632                                  const gchar *old_name,
633                                  const gchar *new_name)
634 {
635     g_assert (BUS_IS_REGISTRY (registry));
636     g_assert (name);
637     g_assert (old_name);
638     g_assert (new_name);
639
640     BusComponent *component;
641     BusFactoryProxy *factory;
642
643     component = bus_registry_lookup_component_by_name (registry, name);
644
645     if (component == NULL) {
646         /* name is a unique name, or a well-known name we don't know. */
647         return;
648     }
649
650     if (g_strcmp0 (old_name, "") != 0) {
651         /* the component is stopped. */
652         factory = bus_component_get_factory (component);
653
654         if (factory != NULL) {
655             ibus_proxy_destroy ((IBusProxy *) factory);
656         }
657     }
658
659     if (g_strcmp0 (new_name, "") != 0) {
660         /* the component is started. */
661         BusConnection *connection =
662                 bus_dbus_impl_get_connection_by_name (BUS_DEFAULT_DBUS,
663                                                       new_name);
664         if (connection == NULL)
665             return;
666
667         factory = bus_factory_proxy_new (connection);
668         if (factory == NULL)
669             return;
670         bus_component_set_factory (component, factory);
671         g_object_unref (factory);
672     }
673 }