aboutsummaryrefslogtreecommitdiff
blob: 38c3fe9865a91b43524995815903cab25d597412 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
From 62cd0492e3459416e1775aedc327bced53f66828 Mon Sep 17 00:00:00 2001
From: Rui Matos <tiagomatos@gmail.com>
Date: Wed, 9 Jan 2013 10:14:55 -0500
Subject: [PATCH] client: Queue events while the IBus context isn't ready

There are actually 3 patches here.

---
client: Queue events while the IBus context isn't ready

We may lose events that ought to be processed while the IBus context
isn't ready or if the connection to IBus isn't fully established yet.

To avoid that, enqueue events to be processed later when the IBus
context creation finishes.

---
client: Don't cancel an ongoing create input context on another request

This would only add more delays.

---
client: Cancel any ongoing create input context request on finalize

BUG=

Review URL: https://codereview.appspot.com/6988047
Patch from Rui Matos <tiagomatos@gmail.com>.
---
 client/gtk2/ibusimcontext.c |  220 ++++++++++++++++++++++++++-----------------
 1 file changed, 133 insertions(+), 87 deletions(-)

diff --git a/client/gtk2/ibusimcontext.c b/client/gtk2/ibusimcontext.c
index 011676f..94005b7 100644
--- a/client/gtk2/ibusimcontext.c
+++ b/client/gtk2/ibusimcontext.c
@@ -40,6 +40,8 @@
 #  define IDEBUG(a...)
 #endif
 
+#define MAX_QUEUED_EVENTS 20
+
 struct _IBusIMContext {
     GtkIMContext parent;
 
@@ -63,6 +65,7 @@ struct _IBusIMContext {
 
     /* cancellable */
     GCancellable    *cancellable;
+    GQueue          *events_queue;
 };
 
 struct _IBusIMContextClass {
@@ -154,6 +157,8 @@ static void     _slave_preedit_end_cb       (GtkIMContext       *slave,
 static GtkIMContextClass    *parent_class = NULL;
 
 static IBusBus              *_bus = NULL;
+static guint                _daemon_name_watch_id = 0;
+static gboolean             _daemon_is_running = FALSE;
 
 void
 ibus_im_context_register_type (GTypeModule *type_module)
@@ -261,6 +266,46 @@ static void     _slave_preedit_end_cb       (GtkIMContext       *slave,
     gdk_event_free ((GdkEvent *)event);
 }
 
+static gboolean
+_process_key_event (IBusInputContext *context,
+                    GdkEventKey      *event)
+{
+    guint state = event->state;
+    gboolean retval = FALSE;
+
+    if (event->type == GDK_KEY_RELEASE) {
+        state |= IBUS_RELEASE_MASK;
+    }
+
+    if (_use_sync_mode) {
+        retval = ibus_input_context_process_key_event (context,
+            event->keyval,
+            event->hardware_keycode - 8,
+            state);
+    }
+    else {
+        ibus_input_context_process_key_event_async (context,
+            event->keyval,
+            event->hardware_keycode - 8,
+            state,
+            -1,
+            NULL,
+            _process_key_event_done,
+            gdk_event_copy ((GdkEvent *) event));
+
+        retval = TRUE;
+    }
+
+    if (retval) {
+        event->state |= IBUS_HANDLED_MASK;
+    }
+    else {
+        event->state |= IBUS_IGNORED_MASK;
+    }
+
+    return retval;
+}
+
 
 /* emit "retrieve-surrounding" glib signal of GtkIMContext, if
  * context->caps has IBUS_CAP_SURROUNDING_TEXT and the current IBus
@@ -387,38 +432,7 @@ static void     _slave_preedit_end_cb       (GtkIMContext       *slave,
         ibusimcontext->time = event->time;
     }
 
-    guint state = event->state;
-    if (event->type == GDK_KEY_RELEASE) {
-        state |= IBUS_RELEASE_MASK;
-    }
-
-    if (_use_sync_mode) {
-        retval = ibus_input_context_process_key_event (
-                                        ibuscontext,
-                                        event->keyval,
-                                        event->hardware_keycode - 8,
-                                        state);
-    }
-    else {
-        ibus_input_context_process_key_event_async (
-                                        ibuscontext,
-                                        event->keyval,
-                                        event->hardware_keycode - 8,
-                                        state,
-                                        -1,
-                                        NULL,
-                                        _process_key_event_done,
-                                        gdk_event_copy ((GdkEvent *) event));
-        retval = TRUE;
-
-    }
-
-    if (retval) {
-        event->state |= IBUS_HANDLED_MASK;
-    }
-    else {
-        event->state |= IBUS_IGNORED_MASK;
-    }
+    retval = _process_key_event (ibuscontext, event);
 
     if (ibusimcontext != NULL) {
         /* unref ibusimcontext could call ibus_im_context_finalize here
@@ -450,6 +464,23 @@ static void     _slave_preedit_end_cb       (GtkIMContext       *slave,
 }
 
 static void
+daemon_name_appeared (GDBusConnection *connection,
+                      const gchar     *name,
+                      const gchar     *owner,
+                      gpointer         data)
+{
+    _daemon_is_running = TRUE;
+}
+
+static void
+daemon_name_vanished (GDBusConnection *connection,
+                      const gchar     *name,
+                      gpointer         data)
+{
+    _daemon_is_running = FALSE;
+}
+
+static void
 ibus_im_context_class_init (IBusIMContextClass *class)
 {
     IDEBUG ("%s", __FUNCTION__);
@@ -533,6 +564,14 @@ static void     _slave_preedit_end_cb       (GtkIMContext       *slave,
     /* always install snooper */
     if (_key_snooper_id == 0)
         _key_snooper_id = gtk_key_snooper_install (_key_snooper_cb, NULL);
+
+    _daemon_name_watch_id = g_bus_watch_name (G_BUS_TYPE_SESSION,
+                                              IBUS_SERVICE_IBUS,
+                                              G_BUS_NAME_WATCHER_FLAGS_NONE,
+                                              daemon_name_appeared,
+                                              daemon_name_vanished,
+                                              NULL,
+                                              NULL);
 }
 
 static void
@@ -543,6 +582,8 @@ static void     _slave_preedit_end_cb       (GtkIMContext       *slave,
         gtk_key_snooper_remove (_key_snooper_id);
         _key_snooper_id = 0;
     }
+
+    g_bus_unwatch_name (_daemon_name_watch_id);
 }
 
 /* Copied from gtk+2.0-2.20.1/modules/input/imcedilla.c to fix crosbug.com/11421.
@@ -602,6 +643,7 @@ static void     _slave_preedit_end_cb       (GtkIMContext       *slave,
     ibusimcontext->caps = IBUS_CAP_PREEDIT_TEXT | IBUS_CAP_FOCUS;
 #endif
 
+    ibusimcontext->events_queue = g_queue_new ();
 
     // Create slave im context
     ibusimcontext->slave = gtk_im_context_simple_new ();
@@ -651,6 +693,13 @@ static void     _slave_preedit_end_cb       (GtkIMContext       *slave,
 
     g_signal_handlers_disconnect_by_func (_bus, G_CALLBACK (_bus_connected_cb), obj);
 
+    if (ibusimcontext->cancellable != NULL) {
+        /* Cancel any ongoing create input context request */
+        g_cancellable_cancel (ibusimcontext->cancellable);
+        g_object_unref (ibusimcontext->cancellable);
+        ibusimcontext->cancellable = NULL;
+    }
+
     if (ibusimcontext->ibuscontext) {
         ibus_proxy_destroy ((IBusProxy *)ibusimcontext->ibuscontext);
     }
@@ -670,6 +719,9 @@ static void     _slave_preedit_end_cb       (GtkIMContext       *slave,
         pango_attr_list_unref (ibusimcontext->preedit_attrs);
     }
 
+    g_queue_free_full (ibusimcontext->events_queue,
+                       (GDestroyNotify)gdk_event_free);
+
     G_OBJECT_CLASS(parent_class)->finalize (obj);
 }
 
@@ -681,65 +733,56 @@ static void     _slave_preedit_end_cb       (GtkIMContext       *slave,
 
     IBusIMContext *ibusimcontext = IBUS_IM_CONTEXT (context);
 
-    if (G_LIKELY (ibusimcontext->ibuscontext && ibusimcontext->has_focus)) {
-        /* If context does not have focus, ibus will process key event in sync mode.
-         * It is a workaround for increase search in treeview.
-         */
-        gboolean retval = FALSE;
-
-        if (event->state & IBUS_HANDLED_MASK)
-            return TRUE;
+    if (!_daemon_is_running)
+        return gtk_im_context_filter_keypress (ibusimcontext->slave, event);
 
-        if (event->state & IBUS_IGNORED_MASK)
-            return gtk_im_context_filter_keypress (ibusimcontext->slave, event);
+    /* If context does not have focus, ibus will process key event in
+     * sync mode.  It is a workaround for increase search in treeview.
+     */
+    if (!ibusimcontext->has_focus)
+        return gtk_im_context_filter_keypress (ibusimcontext->slave, event);
 
-        /* XXX it is a workaround for some applications do not set client window. */
-        if (ibusimcontext->client_window == NULL && event->window != NULL)
-            gtk_im_context_set_client_window ((GtkIMContext *)ibusimcontext, event->window);
+    if (event->state & IBUS_HANDLED_MASK)
+        return TRUE;
 
-        _request_surrounding_text (ibusimcontext);
+    if (event->state & IBUS_IGNORED_MASK)
+        return gtk_im_context_filter_keypress (ibusimcontext->slave, event);
 
-        if (ibusimcontext != NULL) {
-            ibusimcontext->time = event->time;
-        }
+    /* XXX it is a workaround for some applications do not set client
+     * window. */
+    if (ibusimcontext->client_window == NULL && event->window != NULL)
+        gtk_im_context_set_client_window ((GtkIMContext *)ibusimcontext,
+                                          event->window);
 
-        guint state = event->state;
-        if (event->type == GDK_KEY_RELEASE) {
-            state |= IBUS_RELEASE_MASK;
-        }
+    _request_surrounding_text (ibusimcontext);
 
-        if (_use_sync_mode) {
-            retval = ibus_input_context_process_key_event (
-                                        ibusimcontext->ibuscontext,
-                                        event->keyval,
-                                        event->hardware_keycode - 8,
-                                        state);
-        }
-        else {
-            ibus_input_context_process_key_event_async (
-                                        ibusimcontext->ibuscontext,
-                                        event->keyval,
-                                        event->hardware_keycode - 8,
-                                        state,
-                                        -1,
-                                        NULL,
-                                        _process_key_event_done,
-                                        gdk_event_copy ((GdkEvent *) event));
-            retval = TRUE;
-        }
+    ibusimcontext->time = event->time;
 
-        if (retval) {
-            event->state |= IBUS_HANDLED_MASK;
+    if (ibusimcontext->ibuscontext) {
+        if (_process_key_event (ibusimcontext->ibuscontext, event))
             return TRUE;
-        }
-        else {
-            event->state |= IBUS_IGNORED_MASK;
-            return gtk_im_context_filter_keypress (ibusimcontext->slave, event);
-        }
+        else
+            return gtk_im_context_filter_keypress (ibusimcontext->slave,
+                                                   event);
     }
-    else {
-        return gtk_im_context_filter_keypress (ibusimcontext->slave, event);
+
+    /* At this point we _should_ be waiting for the IBus context to be
+     * created or the connection to IBus to be established. If that's
+     * the case we queue events to be processed when the IBus context
+     * is ready. */
+    g_return_val_if_fail (ibusimcontext->cancellable != NULL ||
+                          ibus_bus_is_connected (_bus) == FALSE,
+                          FALSE);
+    g_queue_push_tail (ibusimcontext->events_queue,
+                       gdk_event_copy ((GdkEvent *)event));
+
+    if (g_queue_get_length (ibusimcontext->events_queue) > MAX_QUEUED_EVENTS) {
+        g_warning ("Events queue growing too big, will start to drop.");
+        gdk_event_free ((GdkEvent *)
+                        g_queue_pop_head (ibusimcontext->events_queue));
     }
+
+    return TRUE;
 }
 
 static void
@@ -1482,6 +1525,14 @@ static void     _slave_preedit_end_cb       (GtkIMContext       *slave,
             ibus_input_context_focus_in (ibusimcontext->ibuscontext);
             _set_cursor_location_internal (ibusimcontext);
         }
+
+        if (!g_queue_is_empty (ibusimcontext->events_queue)) {
+            GdkEventKey *event;
+            while (event = g_queue_pop_head (ibusimcontext->events_queue)) {
+                _process_key_event (context, event);
+                gdk_event_free ((GdkEvent *)event);
+            }
+        }
     }
 
     g_object_unref (ibusimcontext);
@@ -1494,12 +1545,7 @@ static void     _slave_preedit_end_cb       (GtkIMContext       *slave,
 
     g_assert (ibusimcontext->ibuscontext == NULL);
 
-    if (ibusimcontext->cancellable != NULL) {
-        /* Cancel previous create input context request */
-        g_cancellable_cancel (ibusimcontext->cancellable);
-        g_object_unref (ibusimcontext->cancellable);
-        ibusimcontext->cancellable = NULL;
-    }
+    g_return_if_fail (ibusimcontext->cancellable == NULL);
 
     ibusimcontext->cancellable = g_cancellable_new ();
 
-- 
1.7.10