1 /* -*- mode: C; c-basic-offset: 4; indent-tabs-mode: nil; -*- */
2 /* vim:set et sts=4: */
3 /* ibus - The Input Bus
4  * Copyright (C) 2008-2014 Peng Huang <shawn.p.huang@gmail.com>
5  * Copyright (C) 2017-2018 Takao Fujiwara <takao.fujiwara1@gmail.com>
6  * Copyright (C) 2008-2018 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 "panelproxy.h"
24 
25 #include "global.h"
26 #include "marshalers.h"
27 #include "types.h"
28 
29 /* panelproxy.c is a very simple proxy class for the panel component that does only the following:
30  *
31  * 1. Handle D-Bus signals from the panel process. For the list of the D-Bus signals, you can check the bus_panel_proxy_g_signal function, or
32  *    introspection_xml in src/ibuspanelservice.c. The bus_panel_proxy_g_signal function simply emits a corresponding glib signal for each
33  *    D-Bus signal.
34  * 2. Handle glib signals for a BusPanelProxy object (which is usually emitted by bus_panel_proxy_g_signal.) The list of such glib signals is
35  *    in the bus_panel_proxy_class_init function. The signal handler function, e.g. bus_panel_proxy_candidate_clicked, simply calls the
36  *    corresponding function in inputcontext.c, e.g. bus_input_context_candidate_clicked, using the current focused context.
37  * 3. Provide a way to call D-Bus methods in the panel process. For the list of the D-Bus methods, you can check the header file (panelproxy.h)
38  *    or introspection_xml in src/ibuspanelservice.c. Functions that calls g_dbus_proxy_call, e.g. bus_panel_proxy_set_cursor_location, would
39  *    fall into this category.
40  * 4. Handle glib signals for a BusInputContext object. The list of such glib signals is in the input_context_signals[] array. The signal handler
41  *    function, e.g. _context_set_cursor_location_cb, simply invokes a D-Bus method by calling a function like bus_panel_proxy_set_cursor_location.
42  */
43 
44 enum {
45     PAGE_UP,
46     PAGE_DOWN,
47     CURSOR_UP,
48     CURSOR_DOWN,
49     CANDIDATE_CLICKED,
50     PROPERTY_ACTIVATE,
51     PROPERTY_SHOW,
52     PROPERTY_HIDE,
53     COMMIT_TEXT,
54     PANEL_EXTENSION,
55     PANEL_EXTENSION_REGISTER_KEYS,
56     UPDATE_PREEDIT_TEXT_RECEIVED,
57     UPDATE_LOOKUP_TABLE_RECEIVED,
58     UPDATE_AUXILIARY_TEXT_RECEIVED,
59     LAST_SIGNAL,
60 };
61 
62 struct _BusPanelProxy {
63     IBusProxy parent;
64 
65     /* instance members */
66     BusInputContext *focused_context;
67     PanelType panel_type;
68 };
69 
70 struct _BusPanelProxyClass {
71     IBusProxyClass parent;
72     /* class members */
73 
74     void (* page_up)            (BusPanelProxy   *panel);
75     void (* page_down)          (BusPanelProxy   *panel);
76     void (* cursor_up)          (BusPanelProxy   *panel);
77     void (* cursor_down)        (BusPanelProxy   *panel);
78     void (* candidate_clicked)  (BusPanelProxy   *panel,
79                                  guint            index,
80                                  guint            button,
81                                  guint            state);
82 
83     void (* property_activate)  (BusPanelProxy   *panel,
84                                  const gchar     *prop_name,
85                                  gint             prop_state);
86     void (* commit_text)        (BusPanelProxy   *panel,
87                                  IBusText        *text);
88 };
89 
90 static guint    panel_signals[LAST_SIGNAL] = { 0 };
91 
92 /* functions prototype */
93 static void     bus_panel_proxy_init            (BusPanelProxy          *panel);
94 static void     bus_panel_proxy_real_destroy    (IBusProxy              *proxy);
95 static void     bus_panel_proxy_g_signal        (GDBusProxy             *proxy,
96                                                  const gchar            *sender_name,
97                                                  const gchar            *signal_name,
98                                                  GVariant               *parameters);
99 static void     bus_panel_proxy_page_up         (BusPanelProxy          *panel);
100 static void     bus_panel_proxy_page_down       (BusPanelProxy          *panel);
101 static void     bus_panel_proxy_cursor_up       (BusPanelProxy          *panel);
102 static void     bus_panel_proxy_cursor_down     (BusPanelProxy          *panel);
103 static void     bus_panel_proxy_candidate_clicked
104                                                 (BusPanelProxy          *panel,
105                                                  guint                   index,
106                                                  guint                   button,
107                                                  guint                   state);
108 static void     bus_panel_proxy_property_activate
109                                                 (BusPanelProxy          *panel,
110                                                  const gchar            *prop_name,
111                                                  gint                    prop_state);
112 static void     bus_panel_proxy_commit_text
113                                                 (BusPanelProxy          *panel,
114                                                  IBusText               *text);
115 
G_DEFINE_TYPE(BusPanelProxy,bus_panel_proxy,IBUS_TYPE_PROXY)116 G_DEFINE_TYPE(BusPanelProxy, bus_panel_proxy, IBUS_TYPE_PROXY)
117 
118 BusPanelProxy *
119 bus_panel_proxy_new (BusConnection *connection,
120                      PanelType      panel_type)
121 {
122     const gchar *path = NULL;
123     GObject *obj;
124     BusPanelProxy *panel;
125 
126     g_assert (BUS_IS_CONNECTION (connection));
127 
128     switch (panel_type) {
129     case PANEL_TYPE_PANEL:
130         path = IBUS_PATH_PANEL;
131         break;
132     case PANEL_TYPE_EXTENSION_EMOJI:
133         path = IBUS_PATH_PANEL_EXTENSION_EMOJI;
134         break;
135     default:
136         g_return_val_if_reached (NULL);
137     }
138 
139     obj = g_initable_new (BUS_TYPE_PANEL_PROXY,
140                           NULL,
141                           NULL,
142                           "g-object-path",     path,
143                           "g-interface-name",  IBUS_INTERFACE_PANEL,
144                           "g-connection",      bus_connection_get_dbus_connection (connection),
145                           "g-default-timeout", g_gdbus_timeout,
146                           "g-flags",           G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START | G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES,
147                           NULL);
148 
149     panel = BUS_PANEL_PROXY (obj);
150     panel->panel_type = panel_type;
151     return panel;
152 }
153 
154 static void
bus_panel_proxy_class_init(BusPanelProxyClass * class)155 bus_panel_proxy_class_init (BusPanelProxyClass *class)
156 {
157     IBUS_PROXY_CLASS (class)->destroy = bus_panel_proxy_real_destroy;
158     G_DBUS_PROXY_CLASS (class)->g_signal = bus_panel_proxy_g_signal;
159 
160     class->page_up     = bus_panel_proxy_page_up;
161     class->page_down   = bus_panel_proxy_page_down;
162     class->cursor_up   = bus_panel_proxy_cursor_up;
163     class->cursor_down = bus_panel_proxy_cursor_down;
164     class->candidate_clicked = bus_panel_proxy_candidate_clicked;
165     class->property_activate = bus_panel_proxy_property_activate;
166     class->commit_text = bus_panel_proxy_commit_text;
167 
168     /* install signals */
169     panel_signals[PAGE_UP] =
170         g_signal_new (I_("page-up"),
171             G_TYPE_FROM_CLASS (class),
172             G_SIGNAL_RUN_LAST,
173             G_STRUCT_OFFSET(BusPanelProxyClass, page_up),
174             NULL, NULL,
175             bus_marshal_VOID__VOID,
176             G_TYPE_NONE, 0);
177 
178     panel_signals[PAGE_DOWN] =
179         g_signal_new (I_("page-down"),
180             G_TYPE_FROM_CLASS (class),
181             G_SIGNAL_RUN_LAST,
182             G_STRUCT_OFFSET(BusPanelProxyClass, page_down),
183             NULL, NULL,
184             bus_marshal_VOID__VOID,
185             G_TYPE_NONE, 0);
186 
187     panel_signals[CURSOR_UP] =
188         g_signal_new (I_("cursor-up"),
189             G_TYPE_FROM_CLASS (class),
190             G_SIGNAL_RUN_LAST,
191             G_STRUCT_OFFSET(BusPanelProxyClass, cursor_up),
192             NULL, NULL,
193             bus_marshal_VOID__VOID,
194             G_TYPE_NONE, 0);
195 
196     panel_signals[CURSOR_DOWN] =
197         g_signal_new (I_("cursor-down"),
198             G_TYPE_FROM_CLASS (class),
199             G_SIGNAL_RUN_LAST,
200             G_STRUCT_OFFSET(BusPanelProxyClass, cursor_down),
201             NULL, NULL,
202             bus_marshal_VOID__VOID,
203             G_TYPE_NONE, 0);
204 
205     panel_signals[CANDIDATE_CLICKED] =
206         g_signal_new (I_("candidate-clicked"),
207             G_TYPE_FROM_CLASS (class),
208             G_SIGNAL_RUN_LAST,
209             G_STRUCT_OFFSET(BusPanelProxyClass, candidate_clicked),
210             NULL, NULL,
211             bus_marshal_VOID__UINT_UINT_UINT,
212             G_TYPE_NONE, 3,
213             G_TYPE_UINT,
214             G_TYPE_UINT,
215             G_TYPE_UINT);
216 
217     panel_signals[PROPERTY_ACTIVATE] =
218         g_signal_new (I_("property-activate"),
219             G_TYPE_FROM_CLASS (class),
220             G_SIGNAL_RUN_LAST,
221             G_STRUCT_OFFSET(BusPanelProxyClass, property_activate),
222             NULL, NULL,
223             bus_marshal_VOID__STRING_INT,
224             G_TYPE_NONE, 2,
225             G_TYPE_STRING,
226             G_TYPE_INT);
227 
228     panel_signals[PROPERTY_SHOW] =
229         g_signal_new (I_("property-show"),
230             G_TYPE_FROM_CLASS (class),
231             G_SIGNAL_RUN_LAST,
232             0,
233             NULL, NULL,
234             bus_marshal_VOID__STRING,
235             G_TYPE_NONE, 1,
236             G_TYPE_STRING);
237 
238     panel_signals[PROPERTY_HIDE] =
239         g_signal_new (I_("property-hide"),
240             G_TYPE_FROM_CLASS (class),
241             G_SIGNAL_RUN_LAST,
242             0,
243             NULL, NULL,
244             bus_marshal_VOID__STRING,
245             G_TYPE_NONE, 1,
246             G_TYPE_STRING);
247 
248     panel_signals[COMMIT_TEXT] =
249         g_signal_new (I_("commit-text"),
250             G_TYPE_FROM_CLASS (class),
251             G_SIGNAL_RUN_LAST,
252             G_STRUCT_OFFSET(BusPanelProxyClass, commit_text),
253             NULL, NULL,
254             bus_marshal_VOID__OBJECT,
255             G_TYPE_NONE, 1,
256             IBUS_TYPE_TEXT);
257 
258     panel_signals[PANEL_EXTENSION] =
259         g_signal_new (I_("panel-extension"),
260             G_TYPE_FROM_CLASS (class),
261             G_SIGNAL_RUN_LAST,
262             0,
263             NULL, NULL,
264             bus_marshal_VOID__OBJECT,
265             G_TYPE_NONE, 1,
266             IBUS_TYPE_EXTENSION_EVENT);
267 
268     panel_signals[PANEL_EXTENSION_REGISTER_KEYS] =
269         g_signal_new (I_("panel-extension-register-keys"),
270             G_TYPE_FROM_CLASS (class),
271             G_SIGNAL_RUN_LAST,
272             0,
273             NULL, NULL,
274             bus_marshal_VOID__VARIANT,
275             G_TYPE_NONE, 1,
276             G_TYPE_VARIANT);
277 
278     panel_signals[UPDATE_PREEDIT_TEXT_RECEIVED] =
279         g_signal_new (I_("update-preedit-text-received"),
280             G_TYPE_FROM_CLASS (class),
281             G_SIGNAL_RUN_LAST,
282             0,
283             NULL, NULL,
284             bus_marshal_VOID__OBJECT_UINT_BOOLEAN,
285             G_TYPE_NONE, 3,
286             IBUS_TYPE_TEXT,
287             G_TYPE_UINT,
288             G_TYPE_BOOLEAN);
289 
290     panel_signals[UPDATE_LOOKUP_TABLE_RECEIVED] =
291         g_signal_new (I_("update-lookup-table-received"),
292             G_TYPE_FROM_CLASS (class),
293             G_SIGNAL_RUN_LAST,
294             0,
295             NULL, NULL,
296             bus_marshal_VOID__OBJECT_BOOLEAN,
297             G_TYPE_NONE, 2,
298             IBUS_TYPE_LOOKUP_TABLE,
299             G_TYPE_BOOLEAN);
300 
301     panel_signals[UPDATE_AUXILIARY_TEXT_RECEIVED] =
302         g_signal_new (I_("update-auxiliary-text-received"),
303             G_TYPE_FROM_CLASS (class),
304             G_SIGNAL_RUN_LAST,
305             0,
306             NULL, NULL,
307             bus_marshal_VOID__OBJECT_BOOLEAN,
308             G_TYPE_NONE, 2,
309             IBUS_TYPE_TEXT,
310             G_TYPE_BOOLEAN);
311 }
312 
313 static void
_g_object_unref_if_floating(gpointer instance)314 _g_object_unref_if_floating (gpointer instance)
315 {
316     if (g_object_is_floating (instance))
317         g_object_unref (instance);
318 }
319 
320 static void
bus_panel_proxy_init(BusPanelProxy * panel)321 bus_panel_proxy_init (BusPanelProxy *panel)
322 {
323     /* member variables will automatically be zero-cleared. */
324 }
325 
326 static void
bus_panel_proxy_real_destroy(IBusProxy * proxy)327 bus_panel_proxy_real_destroy (IBusProxy *proxy)
328 {
329     BusPanelProxy *panel = (BusPanelProxy *)proxy;
330 
331     if (panel->focused_context) {
332         bus_panel_proxy_focus_out (panel, panel->focused_context);
333         panel->focused_context = NULL;
334     }
335 
336     IBUS_PROXY_CLASS(bus_panel_proxy_parent_class)->
337             destroy ((IBusProxy *)panel);
338 }
339 
340 /**
341  * bus_panel_proxy_g_signal:
342  *
343  * Handle all D-Bus signals from the panel process. This function emits a corresponding glib signal for each D-Bus signal.
344  */
345 static void
bus_panel_proxy_g_signal(GDBusProxy * proxy,const gchar * sender_name,const gchar * signal_name,GVariant * parameters)346 bus_panel_proxy_g_signal (GDBusProxy  *proxy,
347                           const gchar *sender_name,
348                           const gchar *signal_name,
349                           GVariant    *parameters)
350 {
351     BusPanelProxy *panel = (BusPanelProxy *)proxy;
352 
353     /* The list of nullary D-Bus signals. */
354     static const struct {
355         const gchar *signal_name;
356         const guint  signal_id;
357     } signals [] = {
358         { "PageUp",         PAGE_UP },
359         { "PageDown",       PAGE_DOWN },
360         { "CursorUp",       CURSOR_UP },
361         { "CursorDown",     CURSOR_DOWN },
362     };
363 
364     gint i;
365     for (i = 0; i < G_N_ELEMENTS (signals); i++) {
366         if (g_strcmp0 (signal_name, signals[i].signal_name) == 0) {
367             g_signal_emit (panel, panel_signals[signals[i].signal_id], 0);
368             return;
369         }
370     }
371 
372     /* Handle D-Bus signals with parameters. Deserialize them and emit a glib signal. */
373     if (g_strcmp0 ("CandidateClicked", signal_name) == 0) {
374         guint index = 0;
375         guint button = 0;
376         guint state = 0;
377         g_variant_get (parameters, "(uuu)", &index, &button, &state);
378         g_signal_emit (panel, panel_signals[CANDIDATE_CLICKED], 0, index, button, state);
379         return;
380     }
381 
382     if (g_strcmp0 ("PropertyActivate", signal_name) == 0) {
383         gchar *prop_name = NULL;
384         gint prop_state = 0;
385         g_variant_get (parameters, "(&su)", &prop_name, &prop_state);
386         g_signal_emit (panel, panel_signals[PROPERTY_ACTIVATE], 0, prop_name, prop_state);
387         return;
388     }
389 
390     if (g_strcmp0 ("PropertyShow", signal_name) == 0) {
391         gchar *prop_name = NULL;
392         g_variant_get (parameters, "(&s)", &prop_name);
393         g_signal_emit (panel, panel_signals[PROPERTY_SHOW], 0, prop_name);
394         return;
395     }
396 
397     if (g_strcmp0 ("PropertyHide", signal_name) == 0) {
398         gchar *prop_name = NULL;
399         g_variant_get (parameters, "(&s)", &prop_name);
400         g_signal_emit (panel, panel_signals[PROPERTY_HIDE], 0, prop_name);
401         return;
402     }
403 
404     if (g_strcmp0 ("CommitText", signal_name) == 0) {
405         GVariant *arg0 = NULL;
406 
407         g_variant_get (parameters, "(v)", &arg0);
408         g_return_if_fail (arg0);
409         IBusText *text = IBUS_TEXT (ibus_serializable_deserialize (arg0));
410         g_variant_unref (arg0);
411         g_return_if_fail (text);
412         g_signal_emit (panel, panel_signals[COMMIT_TEXT], 0, text);
413         _g_object_unref_if_floating (text);
414         return;
415     }
416 
417     if (g_strcmp0 ("PanelExtension", signal_name) == 0) {
418         GVariant *arg0 = NULL;
419 
420         g_variant_get (parameters, "(v)", &arg0);
421         g_return_if_fail (arg0);
422         IBusExtensionEvent *event = IBUS_EXTENSION_EVENT (
423                 ibus_serializable_deserialize (arg0));
424         g_variant_unref (arg0);
425         g_return_if_fail (event);
426         g_signal_emit (panel, panel_signals[PANEL_EXTENSION], 0, event);
427         _g_object_unref_if_floating (event);
428         return;
429     }
430 
431     if (g_strcmp0 ("PanelExtensionRegisterKeys", signal_name) == 0) {
432         g_signal_emit (panel, panel_signals[PANEL_EXTENSION_REGISTER_KEYS], 0,
433                        parameters);
434         return;
435     }
436 
437     if (g_strcmp0 ("UpdatePreeditTextReceived", signal_name) == 0) {
438         GVariant *variant = NULL;
439         guint cursor_pos = 0;
440         gboolean visible = FALSE;
441         IBusText *text = NULL;
442 
443         g_variant_get (parameters, "(vub)", &variant, &cursor_pos, &visible);
444         g_return_if_fail (variant);
445         text = (IBusText *) ibus_serializable_deserialize (variant);
446         g_variant_unref (variant);
447         g_return_if_fail (text);
448         g_signal_emit (panel, panel_signals[UPDATE_PREEDIT_TEXT_RECEIVED], 0,
449                        text, cursor_pos, visible);
450         _g_object_unref_if_floating (text);
451         return;
452     }
453 
454     if (g_strcmp0 ("UpdateLookupTableReceived", signal_name) == 0) {
455         GVariant *variant = NULL;
456         gboolean visible = FALSE;
457         IBusLookupTable *table = NULL;
458 
459         g_variant_get (parameters, "(vb)", &variant, &visible);
460         g_return_if_fail (variant);
461         table = (IBusLookupTable *) ibus_serializable_deserialize (variant);
462         g_variant_unref (variant);
463         g_return_if_fail (table);
464         g_signal_emit (panel, panel_signals[UPDATE_LOOKUP_TABLE_RECEIVED], 0,
465                        table, visible);
466         _g_object_unref_if_floating (table);
467         return;
468     }
469 
470     if (g_strcmp0 ("UpdateAuxiliaryTextReceived", signal_name) == 0) {
471         GVariant *variant = NULL;
472         gboolean visible = FALSE;
473         IBusText *text = NULL;
474 
475         g_variant_get (parameters, "(vb)", &variant, &visible);
476         g_return_if_fail (variant);
477         text = (IBusText *) ibus_serializable_deserialize (variant);
478         g_variant_unref (variant);
479         g_return_if_fail (text);
480         g_signal_emit (panel, panel_signals[UPDATE_AUXILIARY_TEXT_RECEIVED], 0,
481                        text, visible);
482         _g_object_unref_if_floating (text);
483         return;
484     }
485 
486     /* shound not be reached */
487     g_return_if_reached ();
488 }
489 
490 
491 void
bus_panel_proxy_set_cursor_location(BusPanelProxy * panel,gint x,gint y,gint w,gint h)492 bus_panel_proxy_set_cursor_location (BusPanelProxy *panel,
493                                      gint           x,
494                                      gint           y,
495                                      gint           w,
496                                      gint           h)
497 {
498     g_assert (BUS_IS_PANEL_PROXY (panel));
499     g_dbus_proxy_call ((GDBusProxy *)panel,
500                        "SetCursorLocation",
501                        g_variant_new ("(iiii)", x, y, w, h),
502                        G_DBUS_CALL_FLAGS_NONE,
503                        -1, NULL, NULL, NULL);
504 }
505 
506 void
bus_panel_proxy_set_cursor_location_relative(BusPanelProxy * panel,gint x,gint y,gint w,gint h)507 bus_panel_proxy_set_cursor_location_relative (BusPanelProxy *panel,
508                                               gint           x,
509                                               gint           y,
510                                               gint           w,
511                                               gint           h)
512 {
513     g_assert (BUS_IS_PANEL_PROXY (panel));
514     g_dbus_proxy_call ((GDBusProxy *)panel,
515                        "SetCursorLocationRelative",
516                        g_variant_new ("(iiii)", x, y, w, h),
517                        G_DBUS_CALL_FLAGS_NONE,
518                        -1, NULL, NULL, NULL);
519 }
520 
521 void
bus_panel_proxy_update_preedit_text(BusPanelProxy * panel,IBusText * text,guint cursor_pos,gboolean visible)522 bus_panel_proxy_update_preedit_text (BusPanelProxy  *panel,
523                                      IBusText       *text,
524                                      guint           cursor_pos,
525                                      gboolean        visible)
526 {
527     g_assert (BUS_IS_PANEL_PROXY (panel));
528     g_assert (IBUS_IS_TEXT (text));
529 
530     GVariant *variant = ibus_serializable_serialize ((IBusSerializable* )text);
531     g_dbus_proxy_call ((GDBusProxy *)panel,
532                        "UpdatePreeditText",
533                        g_variant_new ("(vub)", variant, cursor_pos, visible),
534                        G_DBUS_CALL_FLAGS_NONE,
535                        -1, NULL, NULL, NULL);
536 }
537 
538 void
bus_panel_proxy_update_auxiliary_text(BusPanelProxy * panel,IBusText * text,gboolean visible)539 bus_panel_proxy_update_auxiliary_text (BusPanelProxy *panel,
540                                        IBusText      *text,
541                                        gboolean       visible)
542 {
543     g_assert (BUS_IS_PANEL_PROXY (panel));
544     g_assert (IBUS_IS_TEXT (text));
545 
546     GVariant *variant = ibus_serializable_serialize ((IBusSerializable* )text);
547     g_dbus_proxy_call ((GDBusProxy *)panel,
548                        "UpdateAuxiliaryText",
549                        g_variant_new ("(vb)", variant, visible),
550                        G_DBUS_CALL_FLAGS_NONE,
551                        -1, NULL, NULL, NULL);
552 }
553 
554 void
bus_panel_proxy_update_lookup_table(BusPanelProxy * panel,IBusLookupTable * table,gboolean visible)555 bus_panel_proxy_update_lookup_table (BusPanelProxy   *panel,
556                                      IBusLookupTable *table,
557                                      gboolean         visible)
558 {
559     g_assert (BUS_IS_PANEL_PROXY (panel));
560     g_assert (IBUS_IS_LOOKUP_TABLE (table));
561 
562     GVariant *variant = ibus_serializable_serialize ((IBusSerializable* )table);
563     g_dbus_proxy_call ((GDBusProxy *)panel,
564                        "UpdateLookupTable",
565                        g_variant_new ("(vb)", variant, visible),
566                        G_DBUS_CALL_FLAGS_NONE,
567                        -1, NULL, NULL, NULL);
568 }
569 
570 void
bus_panel_proxy_register_properties(BusPanelProxy * panel,IBusPropList * prop_list)571 bus_panel_proxy_register_properties (BusPanelProxy  *panel,
572                                      IBusPropList   *prop_list)
573 {
574     g_assert (BUS_IS_PANEL_PROXY (panel));
575     g_assert (IBUS_IS_PROP_LIST (prop_list));
576 
577     GVariant *variant = ibus_serializable_serialize ((IBusSerializable *)prop_list);
578     g_dbus_proxy_call ((GDBusProxy *)panel,
579                        "RegisterProperties",
580                        g_variant_new ("(v)", variant),
581                        G_DBUS_CALL_FLAGS_NONE,
582                        -1, NULL, NULL, NULL);
583 }
584 
585 void
bus_panel_proxy_update_property(BusPanelProxy * panel,IBusProperty * prop)586 bus_panel_proxy_update_property (BusPanelProxy  *panel,
587                                  IBusProperty   *prop)
588 {
589     g_assert (BUS_IS_PANEL_PROXY (panel));
590     g_assert (IBUS_IS_PROPERTY (prop));
591 
592     GVariant *variant = ibus_serializable_serialize ((IBusSerializable *)prop);
593     g_dbus_proxy_call ((GDBusProxy *)panel,
594                        "UpdateProperty",
595                        g_variant_new ("(v)", variant),
596                        G_DBUS_CALL_FLAGS_NONE,
597                        -1, NULL, NULL, NULL);
598 }
599 
600 void
bus_panel_proxy_set_content_type(BusPanelProxy * panel,guint purpose,guint hints)601 bus_panel_proxy_set_content_type (BusPanelProxy  *panel,
602                                   guint           purpose,
603                                   guint           hints)
604 {
605     g_assert (BUS_IS_PANEL_PROXY (panel));
606 
607     g_dbus_proxy_call ((GDBusProxy *)panel,
608                        "ContentType",
609                        g_variant_new ("(uu)", purpose, hints),
610                        G_DBUS_CALL_FLAGS_NONE,
611                        -1, NULL, NULL, NULL);
612 }
613 
614 #define DEFINE_FUNC(name)                                       \
615     static void                                                 \
616     bus_panel_proxy_##name (BusPanelProxy *panel)               \
617     {                                                           \
618         g_assert (BUS_IS_PANEL_PROXY (panel));                  \
619                                                                 \
620         if (panel->focused_context) {                           \
621             bus_input_context_##name (panel->focused_context);  \
622         }                                                       \
623     }
624 
625 DEFINE_FUNC(page_up)
DEFINE_FUNC(page_down)626 DEFINE_FUNC(page_down)
627 DEFINE_FUNC(cursor_up)
628 DEFINE_FUNC(cursor_down)
629 #undef DEFINE_FUNC
630 
631 static void
632 bus_panel_proxy_candidate_clicked (BusPanelProxy *panel,
633                                    guint          index,
634                                    guint          button,
635                                    guint          state)
636 {
637     g_assert (BUS_IS_PANEL_PROXY (panel));
638 
639     if (panel->focused_context) {
640         bus_input_context_candidate_clicked (panel->focused_context,
641                                              index,
642                                              button,
643                                              state);
644     }
645 }
646 
647 static void
bus_panel_proxy_property_activate(BusPanelProxy * panel,const gchar * prop_name,gint prop_state)648 bus_panel_proxy_property_activate (BusPanelProxy *panel,
649                                    const gchar   *prop_name,
650                                    gint          prop_state)
651 {
652     g_assert (BUS_IS_PANEL_PROXY (panel));
653 
654     if (panel->focused_context) {
655         bus_input_context_property_activate (panel->focused_context, prop_name, prop_state);
656     }
657 }
658 
659 static void
bus_panel_proxy_commit_text(BusPanelProxy * panel,IBusText * text)660 bus_panel_proxy_commit_text (BusPanelProxy *panel,
661                              IBusText      *text)
662 {
663     gboolean use_extension = TRUE;
664     g_assert (BUS_IS_PANEL_PROXY (panel));
665     g_assert (text != NULL);
666 
667     if (!panel->focused_context)
668         return;
669     if (panel->panel_type != PANEL_TYPE_PANEL)
670         use_extension = FALSE;
671     bus_input_context_commit_text_use_extension (panel->focused_context,
672                                                  text,
673                                                  use_extension);
674 }
675 
676 #define DEFINE_FUNCTION(Name, name)                     \
677     void bus_panel_proxy_##name (BusPanelProxy *panel)  \
678     {                                                   \
679         g_assert (BUS_IS_PANEL_PROXY (panel));          \
680         g_dbus_proxy_call ((GDBusProxy *) panel,        \
681                            #Name,                       \
682                            NULL,                        \
683                            G_DBUS_CALL_FLAGS_NONE,      \
684                            -1, NULL, NULL, NULL);       \
685     }
686 
DEFINE_FUNCTION(ShowPreeditText,show_preedit_text)687 DEFINE_FUNCTION (ShowPreeditText, show_preedit_text)
688 DEFINE_FUNCTION (HidePreeditText, hide_preedit_text)
689 DEFINE_FUNCTION (ShowAuxiliaryText, show_auxiliary_text)
690 DEFINE_FUNCTION (HideAuxiliaryText, hide_auxiliary_text)
691 DEFINE_FUNCTION (ShowLookupTable, show_lookup_table)
692 DEFINE_FUNCTION (HideLookupTable, hide_lookup_table)
693 DEFINE_FUNCTION (PageUpLookupTable, page_up_lookup_table)
694 DEFINE_FUNCTION (PageDownLookupTable, page_down_lookup_table)
695 DEFINE_FUNCTION (CursorUpLookupTable, cursor_up_lookup_table)
696 DEFINE_FUNCTION (CursorDownLookupTable, cursor_down_lookup_table)
697 DEFINE_FUNCTION (StateChanged, state_changed)
698 
699 #undef DEFINE_FUNCTION
700 
701 static void
702 _context_set_cursor_location_cb (BusInputContext *context,
703                                  gint             x,
704                                  gint             y,
705                                  gint             w,
706                                  gint             h,
707                                  BusPanelProxy   *panel)
708 {
709     g_assert (BUS_IS_INPUT_CONTEXT (context));
710     g_assert (BUS_IS_PANEL_PROXY (panel));
711 
712     g_return_if_fail (panel->focused_context == context);
713 
714     bus_panel_proxy_set_cursor_location (panel, x, y, w, h);
715 }
716 
717 static void
_context_set_cursor_location_relative_cb(BusInputContext * context,gint x,gint y,gint w,gint h,BusPanelProxy * panel)718 _context_set_cursor_location_relative_cb (BusInputContext *context,
719                                           gint             x,
720                                           gint             y,
721                                           gint             w,
722                                           gint             h,
723                                           BusPanelProxy   *panel)
724 {
725     g_assert (BUS_IS_INPUT_CONTEXT (context));
726     g_assert (BUS_IS_PANEL_PROXY (panel));
727 
728     g_return_if_fail (panel->focused_context == context);
729 
730     bus_panel_proxy_set_cursor_location_relative (panel, x, y, w, h);
731 }
732 
733 static void
_context_update_preedit_text_cb(BusInputContext * context,IBusText * text,guint cursor_pos,gboolean visible,BusPanelProxy * panel)734 _context_update_preedit_text_cb (BusInputContext *context,
735                                  IBusText        *text,
736                                  guint            cursor_pos,
737                                  gboolean         visible,
738                                  BusPanelProxy   *panel)
739 {
740     g_assert (BUS_IS_INPUT_CONTEXT (context));
741     g_assert (text != NULL);
742     g_assert (BUS_IS_PANEL_PROXY (panel));
743 
744     g_return_if_fail (panel->focused_context == context);
745 
746     /* The callback is called with X11 applications but
747      * the callback is not called for extensions and panel
748      * extensions are always calls by
749      * bus_panel_proxy_update_preedit_text() directly
750      * because panel extensions forward UpdatePreeditText to
751      * UpdatePreeditTextReceived and it can be an infinite
752      * loop.
753      */
754     if (panel->panel_type != PANEL_TYPE_PANEL)
755         return;
756     bus_panel_proxy_update_preedit_text (panel,
757                                          text,
758                                          cursor_pos,
759                                          visible);
760 }
761 
762 static void
_context_update_auxiliary_text_cb(BusInputContext * context,IBusText * text,gboolean visible,BusPanelProxy * panel)763 _context_update_auxiliary_text_cb (BusInputContext *context,
764                                    IBusText        *text,
765                                    gboolean         visible,
766                                    BusPanelProxy   *panel)
767 {
768     g_assert (BUS_IS_INPUT_CONTEXT (context));
769     g_assert (BUS_IS_PANEL_PROXY (panel));
770 
771     g_return_if_fail (panel->focused_context == context);
772 
773     bus_panel_proxy_update_auxiliary_text (panel,
774                                            text,
775                                            visible);
776 }
777 
778 static void
_context_update_lookup_table_cb(BusInputContext * context,IBusLookupTable * table,gboolean visible,BusPanelProxy * panel)779 _context_update_lookup_table_cb (BusInputContext *context,
780                                  IBusLookupTable *table,
781                                  gboolean         visible,
782                                  BusPanelProxy   *panel)
783 {
784     g_assert (BUS_IS_INPUT_CONTEXT (context));
785     g_assert (BUS_IS_PANEL_PROXY (panel));
786 
787     g_return_if_fail (panel->focused_context == context);
788 
789     bus_panel_proxy_update_lookup_table (panel,
790                                          table,
791                                          visible);
792 }
793 
794 static void
_context_register_properties_cb(BusInputContext * context,IBusPropList * prop_list,BusPanelProxy * panel)795 _context_register_properties_cb (BusInputContext *context,
796                                  IBusPropList    *prop_list,
797                                  BusPanelProxy   *panel)
798 {
799     g_assert (BUS_IS_INPUT_CONTEXT (context));
800     g_assert (BUS_IS_PANEL_PROXY (panel));
801 
802     g_return_if_fail (panel->focused_context == context);
803 
804     bus_panel_proxy_register_properties (panel,
805                                          prop_list);
806 }
807 
808 static void
_context_update_property_cb(BusInputContext * context,IBusProperty * prop,BusPanelProxy * panel)809 _context_update_property_cb (BusInputContext *context,
810                              IBusProperty    *prop,
811                              BusPanelProxy   *panel)
812 {
813     g_assert (BUS_IS_INPUT_CONTEXT (context));
814     g_assert (BUS_IS_PANEL_PROXY (panel));
815 
816     g_return_if_fail (panel->focused_context == context);
817 
818     bus_panel_proxy_update_property (panel,
819                                      prop);
820 }
821 
822 static void
_context_destroy_cb(BusInputContext * context,BusPanelProxy * panel)823 _context_destroy_cb (BusInputContext *context,
824                      BusPanelProxy   *panel)
825 {
826     g_assert (BUS_IS_INPUT_CONTEXT (context));
827     g_assert (BUS_IS_PANEL_PROXY (panel));
828 
829     g_assert (context == panel->focused_context);
830 
831     bus_panel_proxy_focus_out (panel, context);
832 }
833 
834 static void
_context_set_content_type_cb(BusInputContext * context,guint purpose,guint hints,BusPanelProxy * panel)835 _context_set_content_type_cb (BusInputContext *context,
836                               guint            purpose,
837                               guint            hints,
838                               BusPanelProxy   *panel)
839 {
840     g_assert (BUS_IS_INPUT_CONTEXT (context));
841     g_assert (BUS_IS_PANEL_PROXY (panel));
842 
843     g_return_if_fail (panel->focused_context == context);
844 
845     bus_panel_proxy_set_content_type (panel, purpose, hints);
846 }
847 
848 #define DEFINE_FUNCTION(name)                                   \
849     static void _context_##name##_cb (BusInputContext *context, \
850                                       BusPanelProxy   *panel)   \
851     {                                                           \
852         g_assert (BUS_IS_INPUT_CONTEXT (context));              \
853         g_assert (BUS_IS_PANEL_PROXY (panel));                  \
854                                                                 \
855         g_return_if_fail (panel->focused_context == context);   \
856                                                                 \
857         bus_panel_proxy_##name (panel);                         \
858     }
859 
860 #define DEFINE_FUNCTION_NO_EXTENSION(name)                      \
861     static void _context_##name##_cb (BusInputContext *context, \
862                                       BusPanelProxy   *panel)   \
863     {                                                           \
864         g_assert (BUS_IS_INPUT_CONTEXT (context));              \
865         g_assert (BUS_IS_PANEL_PROXY (panel));                  \
866                                                                 \
867         g_return_if_fail (panel->focused_context == context);   \
868                                                                 \
869         /* The callback is called with X11 applications but     \
870          * the callback is not called for extensions and panel  \
871          * extensions are always calls by                       \
872          * bus_panel_proxy_update_preedit_text() directly       \
873          * because panel extensions forward UpdatePreeditText to \
874          * UpdatePreeditTextReceived and it can be an infinite  \
875          * loop.                                                \
876          */                                                     \
877         if (panel->panel_type != PANEL_TYPE_PANEL)              \
878             return;                                             \
879         bus_panel_proxy_##name (panel);                         \
880     }
881 
882 
883 DEFINE_FUNCTION_NO_EXTENSION (show_preedit_text)
884 DEFINE_FUNCTION_NO_EXTENSION (hide_preedit_text)
885 DEFINE_FUNCTION (show_auxiliary_text)
886 DEFINE_FUNCTION (hide_auxiliary_text)
887 DEFINE_FUNCTION (show_lookup_table)
888 DEFINE_FUNCTION (hide_lookup_table)
889 DEFINE_FUNCTION (page_up_lookup_table)
890 DEFINE_FUNCTION (page_down_lookup_table)
891 DEFINE_FUNCTION (cursor_up_lookup_table)
892 DEFINE_FUNCTION (cursor_down_lookup_table)
893 DEFINE_FUNCTION (state_changed)
894 
895 #undef DEFINE_FUNCTION
896 #undef DEFINE_FUNCTION_NO_EXTENSION
897 
898 static const struct {
899     gchar *name;
900     GCallback callback;
901 } input_context_signals[] = {
902     { "set-cursor-location",        G_CALLBACK (_context_set_cursor_location_cb) },
903     { "set-cursor-location-relative", G_CALLBACK (_context_set_cursor_location_relative_cb) },
904 
905     { "update-preedit-text",        G_CALLBACK (_context_update_preedit_text_cb) },
906     { "show-preedit-text",          G_CALLBACK (_context_show_preedit_text_cb) },
907     { "hide-preedit-text",          G_CALLBACK (_context_hide_preedit_text_cb) },
908 
909     { "update-auxiliary-text",      G_CALLBACK (_context_update_auxiliary_text_cb) },
910     { "show-auxiliary-text",        G_CALLBACK (_context_show_auxiliary_text_cb) },
911     { "hide-auxiliary-text",        G_CALLBACK (_context_hide_auxiliary_text_cb) },
912 
913     { "update-lookup-table",        G_CALLBACK (_context_update_lookup_table_cb) },
914     { "show-lookup-table",          G_CALLBACK (_context_show_lookup_table_cb) },
915     { "hide-lookup-table",          G_CALLBACK (_context_hide_lookup_table_cb) },
916     { "page-up-lookup-table",       G_CALLBACK (_context_page_up_lookup_table_cb) },
917     { "page-down-lookup-table",     G_CALLBACK (_context_page_down_lookup_table_cb) },
918     { "cursor-up-lookup-table",     G_CALLBACK (_context_cursor_up_lookup_table_cb) },
919     { "cursor-down-lookup-table",   G_CALLBACK (_context_cursor_down_lookup_table_cb) },
920 
921     { "register-properties",        G_CALLBACK (_context_register_properties_cb) },
922     { "update-property",            G_CALLBACK (_context_update_property_cb) },
923 
924     { "engine-changed",             G_CALLBACK (_context_state_changed_cb) },
925 
926     { "destroy",                    G_CALLBACK (_context_destroy_cb) },
927 
928     { "set-content-type",           G_CALLBACK (_context_set_content_type_cb) },
929 };
930 
931 void
bus_panel_proxy_focus_in(BusPanelProxy * panel,BusInputContext * context)932 bus_panel_proxy_focus_in (BusPanelProxy     *panel,
933                           BusInputContext   *context)
934 {
935     const gchar *path;
936     guint purpose, hints;
937     gint i;
938 
939     g_assert (BUS_IS_PANEL_PROXY (panel));
940     g_assert (BUS_IS_INPUT_CONTEXT (context));
941 
942     if (panel->focused_context == context)
943         return;
944 
945     if (panel->focused_context != NULL)
946         bus_panel_proxy_focus_out (panel, panel->focused_context);
947 
948     g_object_ref_sink (context);
949     panel->focused_context = context;
950 
951     path = ibus_service_get_object_path ((IBusService *)context);
952 
953     g_dbus_proxy_call ((GDBusProxy *)panel,
954                        "FocusIn",
955                        g_variant_new ("(o)", path),
956                        G_DBUS_CALL_FLAGS_NONE,
957                        -1, NULL, NULL, NULL);
958 
959     /* install signal handlers */
960     for (i = 0; i < G_N_ELEMENTS (input_context_signals); i++) {
961         g_signal_connect (context,
962                           input_context_signals[i].name,
963                           input_context_signals[i].callback,
964                           panel);
965     }
966 
967     bus_input_context_get_content_type (context, &purpose, &hints);
968     bus_panel_proxy_set_content_type (panel, purpose, hints);
969 }
970 
971 void
bus_panel_proxy_focus_out(BusPanelProxy * panel,BusInputContext * context)972 bus_panel_proxy_focus_out (BusPanelProxy    *panel,
973                            BusInputContext  *context)
974 {
975     g_assert (BUS_IS_PANEL_PROXY (panel));
976     g_assert (BUS_IS_INPUT_CONTEXT (context));
977 
978     g_assert (panel->focused_context == context);
979 
980     /* uninstall signal handlers */
981     gint i;
982     for (i = 0; i < G_N_ELEMENTS (input_context_signals); i++) {
983         g_signal_handlers_disconnect_by_func (context,
984                                               input_context_signals[i].callback,
985                                               panel);
986     }
987 
988     const gchar *path = ibus_service_get_object_path ((IBusService *)context);
989 
990     g_dbus_proxy_call ((GDBusProxy *)panel,
991                        "FocusOut",
992                        g_variant_new ("(o)", path),
993                        G_DBUS_CALL_FLAGS_NONE,
994                        -1, NULL, NULL, NULL);
995 
996     g_object_unref (panel->focused_context);
997     panel->focused_context = NULL;
998 }
999 
1000 void
bus_panel_proxy_destroy_context(BusPanelProxy * panel,BusInputContext * context)1001 bus_panel_proxy_destroy_context (BusPanelProxy    *panel,
1002                                  BusInputContext  *context)
1003 {
1004     const gchar *path;
1005 
1006     g_assert (BUS_IS_PANEL_PROXY (panel));
1007     g_assert (BUS_IS_INPUT_CONTEXT (context));
1008 
1009     g_object_ref_sink (context);
1010     path = ibus_service_get_object_path ((IBusService *)context);
1011 
1012     g_dbus_proxy_call ((GDBusProxy *)panel,
1013                        "DestroyContext",
1014                        g_variant_new ("(o)", path),
1015                        G_DBUS_CALL_FLAGS_NONE,
1016                        -1, NULL, NULL, NULL);
1017 
1018     g_object_unref (context);
1019 }
1020 
1021 PanelType
bus_panel_proxy_get_panel_type(BusPanelProxy * panel)1022 bus_panel_proxy_get_panel_type (BusPanelProxy    *panel)
1023 {
1024     g_assert (BUS_IS_PANEL_PROXY (panel));
1025     return panel->panel_type;
1026 }
1027 
1028 void
bus_panel_proxy_panel_extension_received(BusPanelProxy * panel,IBusExtensionEvent * event)1029 bus_panel_proxy_panel_extension_received (BusPanelProxy      *panel,
1030                                           IBusExtensionEvent *event)
1031 {
1032     GVariant *data;
1033 
1034     g_assert (BUS_IS_PANEL_PROXY (panel));
1035     g_assert (event);
1036 
1037     data = ibus_serializable_serialize (IBUS_SERIALIZABLE (event));
1038     g_return_if_fail (data);
1039     g_dbus_proxy_call ((GDBusProxy *)panel,
1040                        "PanelExtensionReceived",
1041                        g_variant_new ("(v)", data),
1042                        G_DBUS_CALL_FLAGS_NONE,
1043                        -1, NULL, NULL, NULL);
1044 }
1045 
1046 void
bus_panel_proxy_process_key_event(BusPanelProxy * panel,guint keyval,guint keycode,guint state,GAsyncReadyCallback callback,gpointer user_data)1047 bus_panel_proxy_process_key_event (BusPanelProxy       *panel,
1048                                    guint                keyval,
1049                                    guint                keycode,
1050                                    guint                state,
1051                                    GAsyncReadyCallback  callback,
1052                                    gpointer             user_data)
1053 {
1054     g_assert (BUS_IS_PANEL_PROXY (panel));
1055 
1056     g_dbus_proxy_call ((GDBusProxy *)panel,
1057                        "ProcessKeyEvent",
1058                        g_variant_new ("(uuu)", keyval, keycode, state),
1059                        G_DBUS_CALL_FLAGS_NONE,
1060                        -1,
1061                        NULL,
1062                        callback,
1063                        user_data);
1064 }
1065 
1066 void
bus_panel_proxy_commit_text_received(BusPanelProxy * panel,IBusText * text)1067 bus_panel_proxy_commit_text_received (BusPanelProxy *panel,
1068                                       IBusText      *text)
1069 {
1070     GVariant *variant;
1071 
1072     g_assert (BUS_IS_PANEL_PROXY (panel));
1073     g_assert (IBUS_IS_TEXT (text));
1074 
1075     variant = ibus_serializable_serialize (IBUS_SERIALIZABLE (text));
1076     g_dbus_proxy_call ((GDBusProxy *)panel,
1077                        "CommitTextReceived",
1078                        g_variant_new ("(v)", variant),
1079                        G_DBUS_CALL_FLAGS_NONE,
1080                        -1, NULL, NULL, NULL);
1081 }
1082 
1083 void
bus_panel_proxy_candidate_clicked_lookup_table(BusPanelProxy * panel,guint index,guint button,guint state)1084 bus_panel_proxy_candidate_clicked_lookup_table (BusPanelProxy *panel,
1085                                                 guint          index,
1086                                                 guint          button,
1087                                                 guint          state)
1088 {
1089     g_assert (BUS_IS_PANEL_PROXY (panel));
1090 
1091     g_dbus_proxy_call ((GDBusProxy *)panel,
1092                        "CandidateClickedLookupTable",
1093                        g_variant_new ("(uuu)", index, button, state),
1094                        G_DBUS_CALL_FLAGS_NONE,
1095                        -1, NULL, NULL, NULL);
1096 }
1097