1 /* -*- mode: C; c-basic-offset: 4; indent-tabs-mode: nil; -*- */
2 /* vim:set et sts=4: */
4 * Copyright (C) 2008-2010 Peng Huang <shawn.p.huang@gmail.com>
5 * Copyright (C) 2008-2010 Red Hat, Inc.
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.
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.
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.
25 #include <glib/gstdio.h>
31 #include "marshalers.h"
39 static guint _signals[LAST_SIGNAL] = { 0 };
44 /* instance members */
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). */
50 /* a mapping from an engine name (e.g. 'pinyin') to the corresponding IBusEngineDesc object. */
51 GHashTable *engine_table;
53 /* a mapping from GFile to GFileMonitor. */
54 GHashTable *monitor_table;
55 guint monitor_timeout_id;
58 struct _BusRegistryClass {
59 IBusObjectClass parent;
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);
74 G_DEFINE_TYPE (BusRegistry, bus_registry, IBUS_TYPE_OBJECT)
77 bus_registry_class_init (BusRegistryClass *class)
79 GObjectClass *gobject_class = G_OBJECT_CLASS (class);
80 IBusObjectClass *ibus_object_class = IBUS_OBJECT_CLASS (class);
83 g_signal_new (I_("changed"),
84 G_TYPE_FROM_CLASS (gobject_class),
86 0, /* does not associate a method in this class. the "changed" signal would be handled in other classes. */
88 bus_marshal_VOID__VOID,
92 ibus_object_class->destroy = (IBusObjectDestroyFunc) bus_registry_destroy;
96 bus_registry_init (BusRegistry *registry)
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);
109 if (g_strcmp0 (g_cache, "none") == 0) {
110 /* Only load registry, but not read and write cache. */
111 bus_registry_load (registry);
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);
119 /* Load registry from cache. If the cache does not exist or
120 * it is outdated, then generate it.
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);
130 for (p = registry->components; p != NULL; p = p->next) {
131 BusComponent *comp = (BusComponent *) p->data;
132 GList *engines = bus_component_get_engines (comp);
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,
142 g_message ("Engine %s is already registered by other component",
146 g_list_free (engines);
151 bus_registry_remove_all (BusRegistry *registry)
153 g_list_free_full (registry->observed_paths, g_object_unref);
154 registry->observed_paths = NULL;
156 g_list_free_full (registry->components, g_object_unref);
157 registry->components = NULL;
159 g_hash_table_remove_all (registry->engine_table);
160 g_hash_table_remove_all (registry->monitor_table);
164 bus_registry_destroy (BusRegistry *registry)
166 bus_registry_remove_all (registry);
168 g_hash_table_destroy (registry->engine_table);
169 registry->engine_table = NULL;
171 g_hash_table_destroy (registry->monitor_table);
172 registry->monitor_table = NULL;
174 if (registry->monitor_timeout_id > 0) {
175 g_source_remove (registry->monitor_timeout_id);
176 registry->monitor_timeout_id = 0;
179 IBUS_OBJECT_CLASS (bus_registry_parent_class)->destroy (IBUS_OBJECT (registry));
185 * Read all XML files in the PKGDATADIR (typically /usr/share/ibus/components/ *.xml) and update the registry object.
188 bus_registry_load (BusRegistry *registry)
190 g_assert (BUS_IS_REGISTRY (registry));
194 gchar **d, **search_path;
196 path = g_ptr_array_new();
198 envstr = g_getenv ("IBUS_COMPONENT_PATH");
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);
207 dirname = g_build_filename (PKGDATADIR, "component", NULL);
208 g_ptr_array_add (path, dirname);
211 /* FIXME Should we support install some IME in user dir? */
212 dirname = g_build_filename (g_get_user_data_dir (),
215 g_ptr_array_add (path, dirname);
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);
224 registry->observed_paths = g_list_append (registry->observed_paths,
227 bus_registry_load_in_dir (registry, *d);
229 g_strfreev (search_path);
232 #define g_string_append_indent(string, indent) \
235 for (i = 0; i < (indent); i++) { \
236 g_string_append (string, " "); \
241 bus_registry_load_cache (BusRegistry *registry)
243 g_assert (BUS_IS_REGISTRY (registry));
249 filename = g_build_filename (g_get_user_cache_dir (), "ibus", "bus", "registry.xml", NULL);
250 node = ibus_xml_parse_file (filename);
257 if (g_strcmp0 (node->name, "ibus-registry") != 0) {
258 ibus_xml_free (node);
262 for (p = node->sub_nodes; p != NULL; p = p->next) {
263 XMLNode *sub_node = (XMLNode *) p->data;
265 if (g_strcmp0 (sub_node->name, "observed-paths") == 0) {
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);
271 g_object_ref_sink (path);
272 registry->observed_paths = g_list_append (registry->observed_paths, path);
277 if (g_strcmp0 (sub_node->name, "components") == 0) {
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);
283 BusComponent *buscomp = bus_component_new (component,
285 g_object_ref_sink (buscomp);
286 registry->components =
287 g_list_append (registry->components, buscomp);
293 g_warning ("Unknown element <%s>", sub_node->name);
296 ibus_xml_free (node);
301 bus_registry_check_modification (BusRegistry *registry)
305 for (p = registry->observed_paths; p != NULL; p = p->next) {
306 if (ibus_observed_path_check_modification ((IBusObservedPath *) p->data))
310 for (p = registry->components; p != NULL; p = p->next) {
311 if (ibus_component_check_modification (bus_component_get_component ((BusComponent *) p->data)))
319 bus_registry_save_cache (BusRegistry *registry)
321 g_assert (BUS_IS_REGISTRY (registry));
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");
338 g_warning ("create registry.xml failed");
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"
347 g_string_append (output, "<ibus-registry>\n");
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,
356 g_string_append_indent (output, 1);
357 g_string_append (output, "</observed-paths>\n");
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),
367 g_string_append_indent (output, 1);
368 g_string_append (output, "</components>\n");
371 g_string_append (output, "</ibus-registry>\n");
372 items = fwrite (output->str, output->len, 1, pf);
373 g_string_free (output, TRUE);
375 return (items == 1 ? TRUE : FALSE);
379 * bus_registry_load_in_dir:
381 * Read all XML files in dirname, create a BusComponent object for each file, and add the component objects to the registry.
384 bus_registry_load_in_dir (BusRegistry *registry,
385 const gchar *dirname)
387 g_assert (BUS_IS_REGISTRY (registry));
390 GError *error = NULL;
392 const gchar *filename;
394 dir = g_dir_open (dirname, 0, &error);
397 g_warning ("Unable open directory %s : %s", dirname, error->message);
398 g_error_free (error);
402 while ((filename = g_dir_read_name (dir)) != NULL) {
405 IBusComponent *component;
407 size = g_utf8_strlen (filename, -1);
408 if (g_strcmp0 (MAX (filename, filename + size - 4), ".xml") != 0)
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,
416 g_object_ref_sink (buscomp);
417 registry->components =
418 g_list_append (registry->components, buscomp);
429 bus_registry_new (void)
431 BusRegistry *registry;
432 registry = (BusRegistry *) g_object_new (BUS_TYPE_REGISTRY, NULL);
437 bus_register_component_is_name_cb (BusComponent *component,
440 g_assert (BUS_IS_COMPONENT (component));
443 return g_strcmp0 (bus_component_get_name (component), name);
447 bus_registry_lookup_component_by_name (BusRegistry *registry,
450 g_assert (BUS_IS_REGISTRY (registry));
454 p = g_list_find_custom (registry->components,
456 (GCompareFunc) bus_register_component_is_name_cb);
458 return (BusComponent *) p->data;
466 bus_registry_get_components (BusRegistry *registry)
468 g_assert (BUS_IS_REGISTRY (registry));
470 return g_list_copy (registry->components);
474 bus_registry_get_engines (BusRegistry *registry)
476 g_assert (BUS_IS_REGISTRY (registry));
478 return g_hash_table_get_values (registry->engine_table);
482 bus_registry_get_engines_by_language (BusRegistry *registry,
483 const gchar *language)
485 g_assert (BUS_IS_REGISTRY (registry));
489 GList *p1, *p2, *engines;
491 n = strlen (language);
493 p1 = bus_registry_get_engines (registry);
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);
509 bus_registry_find_engine_by_name (BusRegistry *registry,
512 g_assert (BUS_IS_REGISTRY (registry));
515 return (IBusEngineDesc *) g_hash_table_lookup (registry->engine_table, name);
519 bus_registry_stop_all_components (BusRegistry *registry)
521 g_assert (BUS_IS_REGISTRY (registry));
523 g_list_foreach (registry->components, (GFunc) bus_component_stop, NULL);
528 _monitor_timeout_cb (BusRegistry *registry)
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;
538 _monitor_changed_cb (GFileMonitor *monitor,
541 GFileMonitorEvent event_type,
542 BusRegistry *registry)
544 g_assert (BUS_IS_REGISTRY (registry));
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)
552 /* Merge successive file changes into one, with a low priority
554 if (registry->monitor_timeout_id > 0)
557 registry->monitor_timeout_id =
558 g_timeout_add_full (G_PRIORITY_DEFAULT_IDLE,
560 (GSourceFunc) _monitor_timeout_cb,
561 g_object_ref (registry),
562 (GDestroyNotify) g_object_unref);
566 * bus_registry_start_monitor_changes:
568 * Start the monitor thread.
571 bus_registry_start_monitor_changes (BusRegistry *registry)
573 GList *observed_paths, *p;
575 g_assert (BUS_IS_REGISTRY (registry));
577 g_hash_table_remove_all (registry->monitor_table);
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);
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;
597 monitor = g_file_monitor (file,
602 if (monitor != NULL) {
603 g_signal_connect (monitor, "changed",
604 G_CALLBACK (_monitor_changed_cb),
607 g_hash_table_replace (registry->monitor_table,
611 g_warning ("Can't monitor directory %s: %s",
614 g_error_free (error);
617 g_object_unref (file);
619 g_list_free (observed_paths);
623 bus_registry_is_changed (BusRegistry *registry)
625 g_assert (BUS_IS_REGISTRY (registry));
626 return (registry->changed != 0);
630 bus_registry_name_owner_changed (BusRegistry *registry,
632 const gchar *old_name,
633 const gchar *new_name)
635 g_assert (BUS_IS_REGISTRY (registry));
640 BusComponent *component;
641 BusFactoryProxy *factory;
643 component = bus_registry_lookup_component_by_name (registry, name);
645 if (component == NULL) {
646 /* name is a unique name, or a well-known name we don't know. */
650 if (g_strcmp0 (old_name, "") != 0) {
651 /* the component is stopped. */
652 factory = bus_component_get_factory (component);
654 if (factory != NULL) {
655 ibus_proxy_destroy ((IBusProxy *) factory);
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,
664 if (connection == NULL)
667 factory = bus_factory_proxy_new (connection);
670 bus_component_set_factory (component, factory);
671 g_object_unref (factory);