Left: | ||
Right: |
OLD | NEW |
---|---|
1 /* -*- mode: C; c-basic-offset: 4; indent-tabs-mode: nil; -*- */ | 1 /* -*- mode: C; c-basic-offset: 4; indent-tabs-mode: nil; -*- */ |
2 /* vim:set et sts=4: */ | 2 /* vim:set et sts=4: */ |
3 /* bus - The Input Bus | 3 /* bus - The Input Bus |
4 * Copyright (C) 2008-2010 Peng Huang <shawn.p.huang@gmail.com> | 4 * Copyright (C) 2008-2010 Peng Huang <shawn.p.huang@gmail.com> |
5 * Copyright (C) 2008-2010 Red Hat, Inc. | 5 * Copyright (C) 2008-2010 Red Hat, Inc. |
6 * | 6 * |
7 * This library is free software; you can redistribute it and/or | 7 * This library is free software; you can redistribute it and/or |
8 * modify it under the terms of the GNU Lesser General Public | 8 * modify it under the terms of the GNU Lesser General Public |
9 * License as published by the Free Software Foundation; either | 9 * License as published by the Free Software Foundation; either |
10 * version 2 of the License, or (at your option) any later version. | 10 * version 2 of the License, or (at your option) any later version. |
(...skipping 31 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
42 IBusObject parent; | 42 IBusObject parent; |
43 | 43 |
44 /* instance members */ | 44 /* instance members */ |
45 | 45 |
46 /* a list of IBusObservedPath objects. */ | 46 /* a list of IBusObservedPath objects. */ |
47 GList *observed_paths; | 47 GList *observed_paths; |
48 /* a list of BusComponent objects that are created from component XML files (or from the cache of them). */ | 48 /* a list of BusComponent objects that are created from component XML files (or from the cache of them). */ |
49 GList *components; | 49 GList *components; |
50 /* a mapping from an engine name (e.g. 'pinyin') to the corresponding IBusEn gineDesc object. */ | 50 /* a mapping from an engine name (e.g. 'pinyin') to the corresponding IBusEn gineDesc object. */ |
51 GHashTable *engine_table; | 51 GHashTable *engine_table; |
52 | |
53 #ifdef G_THREADS_ENABLED | |
54 GThread *thread; | |
55 gboolean thread_running; | |
56 GMutex *mutex; | |
57 GCond *cond; | |
58 gboolean changed; | 52 gboolean changed; |
59 #endif | 53 /* a mapping from GFile to GFileMonitor. */ |
54 GHashTable *monitor_table; | |
55 guint monitor_timeout_id; | |
60 }; | 56 }; |
61 | 57 |
62 struct _BusRegistryClass { | 58 struct _BusRegistryClass { |
63 IBusObjectClass parent; | 59 IBusObjectClass parent; |
64 | 60 |
65 /* class members */ | 61 /* class members */ |
66 }; | 62 }; |
67 | 63 |
68 /* functions prototype */ | 64 /* functions prototype */ |
69 static void bus_registry_destroy (BusRegistry *reg istry); | 65 static void bus_registry_destroy (BusRegistry *reg istry); |
(...skipping 26 matching lines...) Expand all Loading... | |
96 ibus_object_class->destroy = (IBusObjectDestroyFunc) bus_registry_destroy; | 92 ibus_object_class->destroy = (IBusObjectDestroyFunc) bus_registry_destroy; |
97 } | 93 } |
98 | 94 |
99 static void | 95 static void |
100 bus_registry_init (BusRegistry *registry) | 96 bus_registry_init (BusRegistry *registry) |
101 { | 97 { |
102 GList *p; | 98 GList *p; |
103 registry->observed_paths = NULL; | 99 registry->observed_paths = NULL; |
104 registry->components = NULL; | 100 registry->components = NULL; |
105 registry->engine_table = g_hash_table_new (g_str_hash, g_str_equal); | 101 registry->engine_table = g_hash_table_new (g_str_hash, g_str_equal); |
106 | |
107 #ifdef G_THREADS_ENABLED | |
108 /* If glib supports thread, we'll create a thread to monitor changes in IME | |
109 * XML files and related files, so users can use newly installed IMEs | |
110 * immediatlly. | |
111 * Note that we don't use GFileMonitor for watching as we need to monitor | |
112 * not only XML files but also other related files that can be scattered in | |
113 * many places. Monitoring these files with GFileMonitor would be make it | |
114 * complicated. hence we use a thread to poll the changes. | |
115 */ | |
116 registry->thread = NULL; | |
117 registry->thread_running = TRUE; | |
118 registry->mutex = g_mutex_new (); | |
119 registry->cond = g_cond_new (); | |
120 registry->changed = FALSE; | 102 registry->changed = FALSE; |
121 #endif | 103 registry->monitor_table = |
104 g_hash_table_new_full (g_file_hash, | |
105 (GEqualFunc) g_file_equal, | |
106 NULL, | |
107 (GDestroyNotify) g_object_unref); | |
122 | 108 |
123 if (g_strcmp0 (g_cache, "none") == 0) { | 109 if (g_strcmp0 (g_cache, "none") == 0) { |
124 /* Only load registry, but not read and write cache. */ | 110 /* Only load registry, but not read and write cache. */ |
125 bus_registry_load (registry); | 111 bus_registry_load (registry); |
126 } | 112 } |
127 else if (g_strcmp0 (g_cache, "refresh") == 0) { | 113 else if (g_strcmp0 (g_cache, "refresh") == 0) { |
128 /* Load registry and overwrite the cache. */ | 114 /* Load registry and overwrite the cache. */ |
129 bus_registry_load (registry); | 115 bus_registry_load (registry); |
130 bus_registry_save_cache (registry); | 116 bus_registry_save_cache (registry); |
131 } | 117 } |
(...skipping 32 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
164 static void | 150 static void |
165 bus_registry_remove_all (BusRegistry *registry) | 151 bus_registry_remove_all (BusRegistry *registry) |
166 { | 152 { |
167 g_list_free_full (registry->observed_paths, g_object_unref); | 153 g_list_free_full (registry->observed_paths, g_object_unref); |
168 registry->observed_paths = NULL; | 154 registry->observed_paths = NULL; |
169 | 155 |
170 g_list_free_full (registry->components, g_object_unref); | 156 g_list_free_full (registry->components, g_object_unref); |
171 registry->components = NULL; | 157 registry->components = NULL; |
172 | 158 |
173 g_hash_table_remove_all (registry->engine_table); | 159 g_hash_table_remove_all (registry->engine_table); |
160 g_hash_table_remove_all (registry->monitor_table); | |
174 } | 161 } |
175 | 162 |
176 static void | 163 static void |
177 bus_registry_destroy (BusRegistry *registry) | 164 bus_registry_destroy (BusRegistry *registry) |
178 { | 165 { |
179 #ifdef G_THREADS_ENABLED | |
180 if (registry->thread) { | |
181 g_mutex_lock (registry->mutex); | |
182 registry->thread_running = FALSE; | |
183 g_mutex_unlock (registry->mutex); | |
184 | |
185 /* Raise a signal to cause the loop in _checks_changes() exits | |
186 * immediately, and then wait until the thread finishes, and release all | |
187 * resources of the thread. | |
188 */ | |
189 g_cond_signal (registry->cond); | |
190 g_thread_join (registry->thread); | |
191 | |
192 registry->thread = NULL; | |
193 } | |
194 #endif | |
195 | |
196 bus_registry_remove_all (registry); | 166 bus_registry_remove_all (registry); |
197 | 167 |
198 g_hash_table_destroy (registry->engine_table); | 168 g_hash_table_destroy (registry->engine_table); |
199 registry->engine_table = NULL; | 169 registry->engine_table = NULL; |
200 | 170 |
201 #ifdef G_THREADS_ENABLED | 171 g_hash_table_destroy (registry->monitor_table); |
202 g_cond_free (registry->cond); | 172 registry->monitor_table = NULL; |
203 registry->cond = NULL; | |
204 | 173 |
205 g_mutex_free (registry->mutex); | 174 if (registry->monitor_timeout_id > 0) { |
206 registry->mutex = NULL; | 175 g_source_remove (registry->monitor_timeout_id); |
207 #endif | 176 registry->monitor_timeout_id = 0; |
177 } | |
208 | 178 |
209 IBUS_OBJECT_CLASS (bus_registry_parent_class)->destroy (IBUS_OBJECT (registr y)); | 179 IBUS_OBJECT_CLASS (bus_registry_parent_class)->destroy (IBUS_OBJECT (registr y)); |
210 } | 180 } |
211 | 181 |
212 /** | 182 /** |
213 * bus_registry_load: | 183 * bus_registry_load: |
214 * | 184 * |
215 * Read all XML files in the PKGDATADIR (typically /usr/share/ibus/components/ * .xml) and update the registry object. | 185 * Read all XML files in the PKGDATADIR (typically /usr/share/ibus/components/ * .xml) and update the registry object. |
216 */ | 186 */ |
217 static void | 187 static void |
(...skipping 325 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
543 | 513 |
544 void | 514 void |
545 bus_registry_stop_all_components (BusRegistry *registry) | 515 bus_registry_stop_all_components (BusRegistry *registry) |
546 { | 516 { |
547 g_assert (BUS_IS_REGISTRY (registry)); | 517 g_assert (BUS_IS_REGISTRY (registry)); |
548 | 518 |
549 g_list_foreach (registry->components, (GFunc) bus_component_stop, NULL); | 519 g_list_foreach (registry->components, (GFunc) bus_component_stop, NULL); |
550 | 520 |
551 } | 521 } |
552 | 522 |
553 #ifdef G_THREADS_ENABLED | |
554 static gboolean | 523 static gboolean |
555 _emit_changed_signal_cb (BusRegistry *registry) | 524 _monitor_timeout_cb (BusRegistry *registry) |
525 { | |
526 g_hash_table_remove_all (registry->monitor_table); | |
527 registry->changed = TRUE; | |
528 g_signal_emit (registry, _signals[CHANGED], 0); | |
529 registry->monitor_timeout_id = 0; | |
530 return FALSE; | |
531 } | |
532 | |
533 static void | |
534 _monitor_changed_cb (GFileMonitor *monitor, | |
535 GFile *file, | |
536 GFile *other_file, | |
537 GFileMonitorEvent event_type, | |
538 BusRegistry *registry) | |
556 { | 539 { |
557 g_assert (BUS_IS_REGISTRY (registry)); | 540 g_assert (BUS_IS_REGISTRY (registry)); |
558 | 541 |
559 g_signal_emit (registry, _signals[CHANGED], 0); | 542 if (event_type != G_FILE_MONITOR_EVENT_CHANGED && |
560 return FALSE; | 543 event_type != G_FILE_MONITOR_EVENT_DELETED && |
561 } | 544 event_type != G_FILE_MONITOR_EVENT_CREATED && |
545 event_type != G_FILE_MONITOR_EVENT_ATTRIBUTE_CHANGED) | |
546 return; | |
562 | 547 |
563 static gpointer | 548 /* Merge successive file changes into one, with a low priority |
564 _check_changes (BusRegistry *registry) | 549 timeout handler. */ |
565 { | 550 if (registry->monitor_timeout_id > 0) |
566 g_assert (BUS_IS_REGISTRY (registry)); | 551 return; |
567 | 552 |
568 g_mutex_lock (registry->mutex); | 553 registry->monitor_timeout_id = |
569 while (registry->thread_running == TRUE && registry->changed == FALSE) { | 554 g_timeout_add_full (G_PRIORITY_DEFAULT_IDLE, |
570 extern gint g_monitor_timeout; | 555 g_monitor_timeout, |
571 GTimeVal tv; | 556 (GSourceFunc) _monitor_timeout_cb, |
572 g_get_current_time (&tv); | 557 g_object_ref (registry), |
573 g_time_val_add (&tv, g_monitor_timeout * G_USEC_PER_SEC); | 558 (GDestroyNotify) g_object_unref); |
574 /* Wait for the condition change or timeout. It will also unlock | |
575 * the mutex, so that other thread could obay the lock and modify | |
576 * the condition value. | |
577 * Note that we use g_cond_timed_wait() here rather than sleep() so | |
578 * that the loop can terminate immediately when the registry object | |
579 * is destroyed. See also comments in bus_registry_destroy(). | |
580 */ | |
581 if (g_cond_timed_wait (registry->cond, registry->mutex, &tv) == FALSE) { | |
582 /* Timeout happens. We check the modification of all IMEs' xml files | |
583 * and related files specified in <observed-paths> in xml files. | |
584 * If any file is changed, the changed signal will be emitted in | |
585 * main thread. It is for finding install/uninstall/upgrade of IMEs. | |
586 * On Linux desktop, ibus will popup UI to notificate users that | |
587 * some IMEs are changed and ibus need a restart. | |
588 */ | |
589 if (bus_registry_check_modification (registry)) { | |
590 /* Emit the changed signal in main thread, and terminate | |
591 * this thread. | |
592 */ | |
593 registry->changed = TRUE; | |
594 g_idle_add ((GSourceFunc) _emit_changed_signal_cb, registry); | |
595 break; | |
596 } | |
597 } | |
598 else | |
599 g_warn_if_fail (registry->thread_running == FALSE); | |
600 } | |
601 g_mutex_unlock (registry->mutex); | |
602 return NULL; | |
603 } | 559 } |
604 | 560 |
605 /** | 561 /** |
606 * bus_registry_start_monitor_changes: | 562 * bus_registry_start_monitor_changes: |
607 * | 563 * |
608 * Start the monitor thread. | 564 * Start the monitor thread. |
609 */ | 565 */ |
610 void | 566 void |
611 bus_registry_start_monitor_changes (BusRegistry *registry) | 567 bus_registry_start_monitor_changes (BusRegistry *registry) |
612 { | 568 { |
569 GList *observed_paths, *p; | |
570 | |
613 g_assert (BUS_IS_REGISTRY (registry)); | 571 g_assert (BUS_IS_REGISTRY (registry)); |
614 | 572 |
615 g_return_if_fail (registry->thread == NULL); | 573 g_hash_table_remove_all (registry->monitor_table); |
616 g_return_if_fail (registry->changed == FALSE); | |
617 | 574 |
618 registry->thread_running = TRUE; | 575 observed_paths = g_list_copy (registry->observed_paths); |
619 registry->thread = g_thread_create ((GThreadFunc) _check_changes, | 576 for (p = registry->components; p != NULL; p = p->next) { |
620 registry, | 577 BusComponent *buscomp = (BusComponent *) p->data; |
621 TRUE, | 578 IBusComponent *component = bus_component_get_component (buscomp); |
622 NULL); | 579 GList *component_observed_paths = |
580 ibus_component_get_observed_paths (component); | |
581 observed_paths = g_list_concat (observed_paths, | |
582 component_observed_paths); | |
583 } | |
584 | |
585 for (p = observed_paths; p != NULL; p = p->next) { | |
586 IBusObservedPath *path = (IBusObservedPath *) p->data; | |
587 GFile *file = g_file_new_for_path (path->path); | |
588 if (!g_hash_table_lookup_extended (registry->monitor_table, | |
Peng
2012/10/23 01:44:27
Does lookup by GFile pointer work? I think two dif
Daiki Ueno
2012/10/23 09:23:02
It should work because g_file_hash and g_file_equa
| |
589 file, | |
590 NULL, | |
591 NULL)) { | |
592 GFileMonitor *monitor; | |
593 GError *error; | |
594 | |
595 error = NULL; | |
596 monitor = g_file_monitor (file, | |
597 G_FILE_MONITOR_NONE, | |
598 NULL, | |
599 &error); | |
600 | |
601 if (monitor != NULL) { | |
602 g_signal_connect (monitor, "changed", | |
603 G_CALLBACK (_monitor_changed_cb), | |
604 registry); | |
605 | |
606 g_hash_table_insert (registry->monitor_table, file, monitor); | |
607 } else { | |
608 g_warning ("Can't monitor directory %s: %s", | |
609 path->path, | |
610 error->message); | |
611 g_error_free (error); | |
612 } | |
613 } | |
614 g_object_unref (file); | |
615 } | |
616 g_list_free (observed_paths); | |
623 } | 617 } |
624 | 618 |
625 gboolean | 619 gboolean |
626 bus_registry_is_changed (BusRegistry *registry) | 620 bus_registry_is_changed (BusRegistry *registry) |
627 { | 621 { |
628 g_assert (BUS_IS_REGISTRY (registry)); | 622 g_assert (BUS_IS_REGISTRY (registry)); |
629 return (registry->changed != 0); | 623 return (registry->changed != 0); |
630 } | 624 } |
631 #endif | |
632 | 625 |
633 void | 626 void |
634 bus_registry_name_owner_changed (BusRegistry *registry, | 627 bus_registry_name_owner_changed (BusRegistry *registry, |
635 const gchar *name, | 628 const gchar *name, |
636 const gchar *old_name, | 629 const gchar *old_name, |
637 const gchar *new_name) | 630 const gchar *new_name) |
638 { | 631 { |
639 g_assert (BUS_IS_REGISTRY (registry)); | 632 g_assert (BUS_IS_REGISTRY (registry)); |
640 g_assert (name); | 633 g_assert (name); |
641 g_assert (old_name); | 634 g_assert (old_name); |
(...skipping 26 matching lines...) Expand all Loading... | |
668 if (connection == NULL) | 661 if (connection == NULL) |
669 return; | 662 return; |
670 | 663 |
671 factory = bus_factory_proxy_new (connection); | 664 factory = bus_factory_proxy_new (connection); |
672 if (factory == NULL) | 665 if (factory == NULL) |
673 return; | 666 return; |
674 bus_component_set_factory (component, factory); | 667 bus_component_set_factory (component, factory); |
675 g_object_unref (factory); | 668 g_object_unref (factory); |
676 } | 669 } |
677 } | 670 } |
OLD | NEW |