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-2013 Peng Huang <shawn.p.huang@gmail.com>
5  * Copyright (C) 2018-2019 Takao Fujiwara <takao.fujiwara1@gmail.com>
6  * Copyright (C) 2008-2019 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 <stdarg.h>
24 #include <string.h>
25 
26 #include "ibusaccelgroup.h"
27 #include "ibusengine.h"
28 #include "ibuskeysyms.h"
29 #include "ibusmarshalers.h"
30 #include "ibusinternal.h"
31 #include "ibusshare.h"
32 #include "ibusxevent.h"
33 
34 #define IBUS_ENGINE_GET_PRIVATE(o)  \
35    ((IBusEnginePrivate *)ibus_engine_get_instance_private (o))
36 
37 enum {
38     PROCESS_KEY_EVENT,
39     FOCUS_IN,
40     FOCUS_OUT,
41     RESET,
42     ENABLE,
43     DISABLE,
44     SET_CURSOR_LOCATION,
45     SET_CAPABILITIES,
46     PAGE_UP,
47     PAGE_DOWN,
48     CURSOR_UP,
49     CURSOR_DOWN,
50     PROPERTY_ACTIVATE,
51     PROPERTY_SHOW,
52     PROPERTY_HIDE,
53     CANDIDATE_CLICKED,
54     SET_SURROUNDING_TEXT,
55     PROCESS_HAND_WRITING_EVENT,
56     CANCEL_HAND_WRITING,
57     SET_CONTENT_TYPE,
58     LAST_SIGNAL,
59 };
60 
61 enum {
62     PROP_0,
63     PROP_ENGINE_NAME,
64 };
65 
66 
67 /* IBusEnginePriv */
68 struct _IBusEnginePrivate {
69     gchar *engine_name;
70     GDBusConnection *connection;
71 
72     /* cached surrounding text (see also IBusInputContextPrivate and
73        BusEngineProxy) */
74     IBusText *surrounding_text;
75     guint surrounding_cursor_pos;
76     guint selection_anchor_pos;
77 
78     /* cached content-type */
79     guint content_purpose;
80     guint content_hints;
81 
82     GHashTable            *extension_keybindings;
83     gboolean               enable_extension;
84     gchar                 *current_extension_name;
85 };
86 
87 
88 static guint            engine_signals[LAST_SIGNAL] = { 0 };
89 
90 static IBusText *text_empty;
91 
92 /* functions prototype */
93 static void      ibus_engine_destroy         (IBusEngine         *engine);
94 static void      ibus_engine_set_property    (IBusEngine         *engine,
95                                               guint               prop_id,
96                                               const GValue       *value,
97                                               GParamSpec         *pspec);
98 static void      ibus_engine_get_property    (IBusEngine         *engine,
99                                               guint               prop_id,
100                                               GValue             *value,
101                                               GParamSpec         *pspec);
102 static void      ibus_engine_service_method_call
103                                               (IBusService        *service,
104                                                GDBusConnection    *connection,
105                                                const gchar        *sender,
106                                                const gchar        *object_path,
107                                                const gchar        *interface_name,
108                                                const gchar        *method_name,
109                                                GVariant           *parameters,
110                                                GDBusMethodInvocation
111                                                                   *invocation);
112 static GVariant *ibus_engine_service_get_property
113                                              (IBusService        *service,
114                                               GDBusConnection    *connection,
115                                               const gchar        *sender,
116                                               const gchar        *object_path,
117                                               const gchar        *interface_name,
118                                               const gchar        *property_name,
119                                               GError            **error);
120 static gboolean  ibus_engine_service_set_property
121                                              (IBusService        *service,
122                                               GDBusConnection    *connection,
123                                               const gchar        *sender,
124                                               const gchar        *object_path,
125                                               const gchar        *interface_name,
126                                               const gchar        *property_name,
127                                               GVariant           *value,
128                                               GError            **error);
129 static gboolean  ibus_engine_process_key_event
130                                              (IBusEngine         *engine,
131                                               guint               keyval,
132                                               guint               keycode,
133                                               guint               state);
134 static void      ibus_engine_focus_in        (IBusEngine         *engine);
135 static void      ibus_engine_focus_out       (IBusEngine         *engine);
136 static void      ibus_engine_reset           (IBusEngine         *engine);
137 static void      ibus_engine_enable          (IBusEngine         *engine);
138 static void      ibus_engine_disable         (IBusEngine         *engine);
139 static void      ibus_engine_set_cursor_location
140                                              (IBusEngine         *engine,
141                                               gint                x,
142                                               gint                y,
143                                               gint                w,
144                                               gint                h);
145 static void      ibus_engine_set_capabilities
146                                              (IBusEngine         *engine,
147                                               guint               caps);
148 static void      ibus_engine_page_up         (IBusEngine         *engine);
149 static void      ibus_engine_page_down       (IBusEngine         *engine);
150 static void      ibus_engine_cursor_up       (IBusEngine         *engine);
151 static void      ibus_engine_cursor_down     (IBusEngine         *engine);
152 static void      ibus_engine_candidate_clicked
153                                              (IBusEngine         *engine,
154                                               guint               index,
155                                               guint               button,
156                                               guint               state);
157 static void      ibus_engine_property_activate
158                                              (IBusEngine         *engine,
159                                               const gchar        *prop_name,
160                                               guint               prop_state);
161 static void      ibus_engine_property_show   (IBusEngine         *engine,
162                                               const gchar        *prop_name);
163 static void      ibus_engine_property_hide   (IBusEngine         *engine,
164                                               const gchar        *prop_name);
165 static void      ibus_engine_set_surrounding_text
166                                             (IBusEngine         *engine,
167                                              IBusText           *text,
168                                              guint               cursor_pos,
169                                              guint               anchor_pos);
170 static void      ibus_engine_process_hand_writing_event
171                                              (IBusEngine         *engine,
172                                               const gdouble      *coordinates,
173                                               guint               coordinates_len);
174 static void      ibus_engine_cancel_hand_writing
175                                              (IBusEngine         *engine,
176                                               guint               n_strokes);
177 static void      ibus_engine_set_content_type
178                                              (IBusEngine         *engine,
179                                               guint               purpose,
180                                               guint               hints);
181 static void      ibus_engine_emit_signal     (IBusEngine         *engine,
182                                               const gchar        *signal_name,
183                                               GVariant           *parameters);
184 static void      ibus_engine_dbus_property_changed
185                                              (IBusEngine         *engine,
186                                               const gchar        *property_name,
187                                               GVariant           *value);
188 
189 
190 G_DEFINE_TYPE_WITH_PRIVATE (IBusEngine, ibus_engine, IBUS_TYPE_SERVICE)
191 
192 static const gchar introspection_xml[] =
193     "<node>"
194     "  <interface name='org.freedesktop.IBus.Engine'>"
195     /* FIXME methods */
196     "    <method name='ProcessKeyEvent'>"
197     "      <arg direction='in'  type='u' name='keyval' />"
198     "      <arg direction='in'  type='u' name='keycode' />"
199     "      <arg direction='in'  type='u' name='state' />"
200     "      <arg direction='out' type='b' />"
201     "    </method>"
202     "    <method name='SetCursorLocation'>"
203     "      <arg direction='in'  type='i' name='x' />"
204     "      <arg direction='in'  type='i' name='y' />"
205     "      <arg direction='in'  type='i' name='w' />"
206     "      <arg direction='in'  type='i' name='h' />"
207     "    </method>"
208     "    <method name='ProcessHandWritingEvent'>"
209     "      <arg direction='in'  type='ad' name='coordinates' />"
210     "    </method>"
211     "    <method name='CancelHandWriting'>"
212     "      <arg direction='in'  type='u' name='n_strokes' />"
213     "    </method>"
214     "    <method name='SetCapabilities'>"
215     "      <arg direction='in'  type='u' name='caps' />"
216     "    </method>"
217     "    <method name='PropertyActivate'>"
218     "      <arg direction='in'  type='s' name='name' />"
219     "      <arg direction='in'  type='u' name='state' />"
220     "    </method>"
221     "    <method name='PropertyShow'>"
222     "      <arg direction='in'  type='s' name='name' />"
223     "    </method>"
224     "    <method name='PropertyHide'>"
225     "      <arg direction='in'  type='s' name='name' />"
226     "    </method>"
227     "    <method name='CandidateClicked'>"
228     "      <arg direction='in'  type='u' name='index' />"
229     "      <arg direction='in'  type='u' name='button' />"
230     "      <arg direction='in'  type='u' name='state' />"
231     "    </method>"
232     "    <method name='FocusIn' />"
233     "    <method name='FocusOut' />"
234     "    <method name='Reset' />"
235     "    <method name='Enable' />"
236     "    <method name='Disable' />"
237     "    <method name='PageUp' />"
238     "    <method name='PageDown' />"
239     "    <method name='CursorUp' />"
240     "    <method name='CursorDown' />"
241     "    <method name='SetSurroundingText'>"
242     "      <arg direction='in'  type='v' name='text' />"
243     "      <arg direction='in'  type='u' name='cursor_pos' />"
244     "      <arg direction='in'  type='u' name='anchor_pos' />"
245     "    </method>"
246     "    <method name='PanelExtensionReceived'>"
247     "      <arg direction='in'  type='v' name='event' />"
248     "    </method>"
249     "    <method name='PanelExtensionRegisterKeys'>"
250     "      <arg direction='in'  type='v' name='data' />"
251     "    </method>"
252     /* FIXME signals */
253     "    <signal name='CommitText'>"
254     "      <arg type='v' name='text' />"
255     "    </signal>"
256     "    <signal name='UpdatePreeditText'>"
257     "      <arg type='v' name='text' />"
258     "      <arg type='u' name='cursor_pos' />"
259     "      <arg type='b' name='visible' />"
260     "      <arg type='u' name='mode' />"
261     "    </signal>"
262     "    <signal name='UpdateAuxiliaryText'>"
263     "      <arg type='v' name='text' />"
264     "      <arg type='b' name='visible' />"
265     "    </signal>"
266     "    <signal name='UpdateLookupTable'>"
267     "      <arg type='v' name='table' />"
268     "      <arg type='b' name='visible' />"
269     "    </signal>"
270     "    <signal name='RegisterProperties'>"
271     "      <arg type='v' name='props' />"
272     "    </signal>"
273     "    <signal name='UpdateProperty'>"
274     "      <arg type='v' name='prop' />"
275     "    </signal>"
276     "    <signal name='ForwardKeyEvent'>"
277     "      <arg type='u' name='keyval' />"
278     "      <arg type='u' name='keycode' />"
279     "      <arg type='u' name='state' />"
280     "    </signal>"
281     "    <signal name='PanelExtension'>"
282     "      <arg type='v' name='data' />"
283     "    </signal>"
284     /* FIXME properties */
285     "    <property name='ContentType' type='(uu)' access='write' />"
286     "  </interface>"
287     "</node>";
288 
289 static const guint IBUS_MODIFIER_FILTER =
290         IBUS_MODIFIER_MASK & ~(
291         IBUS_LOCK_MASK |  /* Caps Lock */
292         IBUS_MOD2_MASK |  /* Num Lock */
293         IBUS_BUTTON1_MASK |
294         IBUS_BUTTON2_MASK |
295         IBUS_BUTTON3_MASK |
296         IBUS_BUTTON4_MASK |
297         IBUS_BUTTON5_MASK |
298         IBUS_SUPER_MASK |
299         IBUS_HYPER_MASK |
300         IBUS_META_MASK);
301 
302 static void
ibus_engine_class_init(IBusEngineClass * class)303 ibus_engine_class_init (IBusEngineClass *class)
304 {
305     GObjectClass *gobject_class = G_OBJECT_CLASS (class);
306     IBusObjectClass *ibus_object_class = IBUS_OBJECT_CLASS (class);
307 
308     gobject_class->set_property =
309             (GObjectSetPropertyFunc) ibus_engine_set_property;
310     gobject_class->get_property =
311             (GObjectGetPropertyFunc) ibus_engine_get_property;
312 
313     ibus_object_class->destroy = (IBusObjectDestroyFunc) ibus_engine_destroy;
314 
315     IBUS_SERVICE_CLASS (class)->service_method_call  =
316             ibus_engine_service_method_call;
317     IBUS_SERVICE_CLASS (class)->service_get_property =
318             ibus_engine_service_get_property;
319     IBUS_SERVICE_CLASS (class)->service_set_property =
320             ibus_engine_service_set_property;
321 
322     ibus_service_class_add_interfaces (IBUS_SERVICE_CLASS (class),
323                                        introspection_xml);
324 
325     class->process_key_event = ibus_engine_process_key_event;
326     class->focus_in     = ibus_engine_focus_in;
327     class->focus_out    = ibus_engine_focus_out;
328     class->reset        = ibus_engine_reset;
329     class->enable       = ibus_engine_enable;
330     class->disable      = ibus_engine_disable;
331     class->page_up      = ibus_engine_page_up;
332     class->page_down    = ibus_engine_page_down;
333     class->cursor_up    = ibus_engine_cursor_up;
334     class->cursor_down  = ibus_engine_cursor_down;
335     class->candidate_clicked    = ibus_engine_candidate_clicked;
336     class->property_activate    = ibus_engine_property_activate;
337     class->property_show        = ibus_engine_property_show;
338     class->property_hide        = ibus_engine_property_hide;
339     class->set_cursor_location  = ibus_engine_set_cursor_location;
340     class->set_capabilities     = ibus_engine_set_capabilities;
341     class->set_surrounding_text = ibus_engine_set_surrounding_text;
342     class->process_hand_writing_event
343                                 = ibus_engine_process_hand_writing_event;
344     class->cancel_hand_writing  = ibus_engine_cancel_hand_writing;
345     class->set_content_type     = ibus_engine_set_content_type;
346 
347     /* install properties */
348     /**
349      * IBusEngine:name:
350      *
351      * Name of this IBusEngine.
352      */
353     g_object_class_install_property (gobject_class,
354                     PROP_ENGINE_NAME,
355                     g_param_spec_string ("engine-name",
356                         "engine name",
357                         "engine name",
358                         "noname",
359                         G_PARAM_READWRITE |
360                         G_PARAM_CONSTRUCT_ONLY |
361                         G_PARAM_STATIC_STRINGS));
362 
363     /* install signals */
364     /**
365      * IBusEngine::process-key-event:
366      * @engine: An IBusEngine.
367      * @keyval: Key symbol of the key press.
368      * @keycode: KeyCode of the key press.
369      * @state: Key modifier flags.
370      *
371      * Emitted when a key event is received.
372      * Implement the member function IBusEngineClass::process_key_event
373      * in extended class to receive this signal.
374      * Both the key symbol and keycode are passed to the member function.
375      * See ibus_input_context_process_key_event() for further explanation of
376      * key symbol, keycode and which to use.
377      *
378      * Returns: %TRUE for successfully process the key; %FALSE otherwise.
379      * See also:  ibus_input_context_process_key_event().
380      *
381      * <note><para>Argument @user_data is ignored in this function.</para></note>
382      */
383     engine_signals[PROCESS_KEY_EVENT] =
384         g_signal_new (I_("process-key-event"),
385             G_TYPE_FROM_CLASS (gobject_class),
386             G_SIGNAL_RUN_LAST,
387             G_STRUCT_OFFSET (IBusEngineClass, process_key_event),
388             g_signal_accumulator_true_handled, NULL,
389             _ibus_marshal_BOOLEAN__UINT_UINT_UINT,
390             G_TYPE_BOOLEAN,
391             3,
392             G_TYPE_UINT,
393             G_TYPE_UINT,
394             G_TYPE_UINT);
395 
396     /**
397      * IBusEngine::focus-in:
398      * @engine: An IBusEngine.
399      *
400      * Emitted when the client application get the focus.
401      * Implement the member function IBusEngineClass::focus_in
402      * in extended class to receive this signal.
403      *
404      * See also: ibus_input_context_focus_in()
405      * <note><para>Argument @user_data is ignored in this function.</para></note>
406      */
407     engine_signals[FOCUS_IN] =
408         g_signal_new (I_("focus-in"),
409             G_TYPE_FROM_CLASS (gobject_class),
410             G_SIGNAL_RUN_LAST,
411             G_STRUCT_OFFSET (IBusEngineClass, focus_in),
412             NULL, NULL,
413             _ibus_marshal_VOID__VOID,
414             G_TYPE_NONE,
415             0);
416 
417     /**
418      * IBusEngine::focus-out:
419      * @engine: An IBusEngine.
420      *
421      * Emitted when the client application  lost the focus.
422      * Implement the member function IBusEngineClass::focus_out
423      * in extended class to receive this signal.
424      *
425      * See also: ibus_input_context_focus_out()
426      * <note><para>Argument @user_data is ignored in this function.</para></note>
427      */
428     engine_signals[FOCUS_OUT] =
429         g_signal_new (I_("focus-out"),
430             G_TYPE_FROM_CLASS (gobject_class),
431             G_SIGNAL_RUN_LAST,
432             G_STRUCT_OFFSET (IBusEngineClass, focus_out),
433             NULL, NULL,
434             _ibus_marshal_VOID__VOID,
435             G_TYPE_NONE,
436             0);
437 
438     /**
439      * IBusEngine::reset:
440      * @engine: An IBusEngine.
441      *
442      * Emitted when the IME is reset.
443      * Implement the member function IBusEngineClass::reset
444      * in extended class to receive this signal.
445      *
446      * See also:  ibus_input_context_reset().
447      * <note><para>Argument @user_data is ignored in this function.</para></note>
448      */
449     engine_signals[RESET] =
450         g_signal_new (I_("reset"),
451             G_TYPE_FROM_CLASS (gobject_class),
452             G_SIGNAL_RUN_LAST,
453             G_STRUCT_OFFSET (IBusEngineClass, reset),
454             NULL, NULL,
455             _ibus_marshal_VOID__VOID,
456             G_TYPE_NONE,
457             0);
458 
459     /**
460      * IBusEngine::enable:
461      * @engine: An IBusEngine.
462      *
463      * Emitted when the IME is enabled.
464      * Implement the member function IBusEngineClass::enable
465      * in extended class to receive this signal.
466      *
467      * See also:  ibus_bus_set_global_engine().
468      * <note><para>Argument @user_data is ignored in this function.</para></note>
469      */
470     engine_signals[ENABLE] =
471         g_signal_new (I_("enable"),
472             G_TYPE_FROM_CLASS (gobject_class),
473             G_SIGNAL_RUN_LAST,
474             G_STRUCT_OFFSET (IBusEngineClass, enable),
475             NULL, NULL,
476             _ibus_marshal_VOID__VOID,
477             G_TYPE_NONE,
478             0);
479 
480     /**
481      * IBusEngine::disable:
482      * @engine: An IBusEngine.
483      *
484      * Emitted when the IME is disabled.
485      * Implement the member function IBusEngineClass::disable
486      * in extended class to receive this signal.
487      *
488      * See also:  ibus_bus_set_global_engine().
489      * <note><para>Argument @user_data is ignored in this function.</para></note>
490      */
491     engine_signals[DISABLE] =
492         g_signal_new (I_("disable"),
493             G_TYPE_FROM_CLASS (gobject_class),
494             G_SIGNAL_RUN_LAST,
495             G_STRUCT_OFFSET (IBusEngineClass, disable),
496             NULL, NULL,
497             _ibus_marshal_VOID__VOID,
498             G_TYPE_NONE,
499             0);
500 
501     /**
502      * IBusEngine::set-cursor-location:
503      * @engine: An IBusEngine.
504      * @x: X coordinate of the cursor.
505      * @y: Y coordinate of the cursor.
506      * @w: Width of the cursor.
507      * @h: Height of the cursor.
508      *
509      * Emitted when the location of IME is set.
510      * Implement the member function IBusEngineClass::set_cursor_location
511      * in extended class to receive this signal.
512      *
513      * See also:  ibus_input_context_set_cursor_location().
514      * <note><para>Argument @user_data is ignored in this function.</para></note>
515      */
516     engine_signals[SET_CURSOR_LOCATION] =
517         g_signal_new (I_("set-cursor-location"),
518             G_TYPE_FROM_CLASS (gobject_class),
519             G_SIGNAL_RUN_LAST,
520             G_STRUCT_OFFSET (IBusEngineClass, set_cursor_location),
521             NULL, NULL,
522             _ibus_marshal_VOID__INT_INT_INT_INT,
523             G_TYPE_NONE,
524             4,
525             G_TYPE_INT,
526             G_TYPE_INT,
527             G_TYPE_INT,
528             G_TYPE_INT);
529 
530     /**
531      * IBusEngine::set-capabilities:
532      * @engine: An IBusEngine.
533      * @caps: Capabilities flags of IBusEngine, see #IBusCapabilite
534      *
535      * Emitted when the client application capabilities is set.
536      * Implement the member function IBusEngineClass::set_capabilities
537      * in extended class to receive this signal.
538      *
539      * See also:  ibus_input_context_set_capabilities().
540      * <note><para>Argument @user_data is ignored in this function.</para></note>
541      */
542     engine_signals[SET_CAPABILITIES] =
543         g_signal_new (I_("set-capabilities"),
544             G_TYPE_FROM_CLASS (gobject_class),
545             G_SIGNAL_RUN_LAST,
546             G_STRUCT_OFFSET (IBusEngineClass, set_capabilities),
547             NULL, NULL,
548             _ibus_marshal_VOID__UINT,
549             G_TYPE_NONE,
550             1,
551             G_TYPE_UINT);
552 
553     /**
554      * IBusEngine::page-up:
555      * @engine: An IBusEngine.
556      *
557      * Emitted when the page-up button is pressed.
558      * Implement the member function IBusEngineClass::page_up
559      * in extended class to receive this signal.
560      *
561      * <note><para>Argument @user_data is ignored in this function.</para></note>
562      */
563     engine_signals[PAGE_UP] =
564         g_signal_new (I_("page-up"),
565             G_TYPE_FROM_CLASS (gobject_class),
566             G_SIGNAL_RUN_LAST,
567             G_STRUCT_OFFSET (IBusEngineClass, page_up),
568             NULL, NULL,
569             _ibus_marshal_VOID__VOID,
570             G_TYPE_NONE,
571             0);
572 
573     /**
574      * IBusEngine::page-down:
575      * @engine: An IBusEngine.
576      *
577      * Emitted when the page-down button is pressed.
578      * Implement the member function IBusEngineClass::page_down
579      * in extended class to receive this signal.
580      *
581      * <note><para>Argument @user_data is ignored in this function.</para></note>
582      */
583     engine_signals[PAGE_DOWN] =
584         g_signal_new (I_("page-down"),
585             G_TYPE_FROM_CLASS (gobject_class),
586             G_SIGNAL_RUN_LAST,
587             G_STRUCT_OFFSET (IBusEngineClass, page_down),
588             NULL, NULL,
589             _ibus_marshal_VOID__VOID,
590             G_TYPE_NONE,
591             0);
592 
593     /**
594      * IBusEngine::cursor-up:
595      * @engine: An IBusEngine.
596      *
597      * Emitted when the up cursor button is pressed.
598      * Implement the member function IBusEngineClass::cursor_up
599      * in extended class to receive this signal.
600      *
601      * <note><para>Argument @user_data is ignored in this function.</para></note>
602      */
603     engine_signals[CURSOR_UP] =
604         g_signal_new (I_("cursor-up"),
605             G_TYPE_FROM_CLASS (gobject_class),
606             G_SIGNAL_RUN_LAST,
607             G_STRUCT_OFFSET (IBusEngineClass, cursor_up),
608             NULL, NULL,
609             _ibus_marshal_VOID__VOID,
610             G_TYPE_NONE,
611             0);
612 
613     /**
614      * IBusEngine::cursor-down:
615      * @engine: An IBusEngine.
616      *
617      * Emitted when the down cursor button is pressed.
618      * Implement the member function IBusEngineClass::cursor_down
619      * in extended class to receive this signal.
620      *
621      * <note><para>Argument @user_data is ignored in this function.</para></note>
622      */
623     engine_signals[CURSOR_DOWN] =
624         g_signal_new (I_("cursor-down"),
625             G_TYPE_FROM_CLASS (gobject_class),
626             G_SIGNAL_RUN_LAST,
627             G_STRUCT_OFFSET (IBusEngineClass, cursor_down),
628             NULL, NULL,
629             _ibus_marshal_VOID__VOID,
630             G_TYPE_NONE,
631             0);
632 
633     /**
634      * IBusEngine::candidate-clicked:
635      * @engine: An IBusEngine.
636      * @index:  Index of candidate be clicked.
637      * @button: Mouse button.
638      * @state:  Keyboard state.
639      *
640      * Emitted when candidate on lookup table is clicked.
641      * Implement the member function IBusEngineClass::candidate_clicked
642      * in extended class to receive this signal.
643      *
644      * <note><para>Argument @user_data is ignored in this function.</para></note>
645      */
646     engine_signals[CANDIDATE_CLICKED] =
647         g_signal_new (I_("candidate-clicked"),
648             G_TYPE_FROM_CLASS (gobject_class),
649             G_SIGNAL_RUN_LAST,
650             G_STRUCT_OFFSET (IBusEngineClass, candidate_clicked),
651             NULL, NULL,
652             _ibus_marshal_VOID__UINT_UINT_UINT,
653             G_TYPE_NONE,
654             3,
655             G_TYPE_UINT,
656             G_TYPE_UINT,
657             G_TYPE_UINT);
658 
659     /**
660      * IBusEngine::property-activate:
661      * @engine: An IBusEngine.
662      * @name:   Property name.
663      * @state:  Property state.
664      *
665      * Emitted when a property is activated or change changed.
666      * Implement the member function IBusEngineClass::property_activate
667      * in extended class to receive this signal.
668      *
669      * <note><para>Argument @user_data is ignored in this function.</para></note>
670      */
671     engine_signals[PROPERTY_ACTIVATE] =
672         g_signal_new (I_("property-activate"),
673             G_TYPE_FROM_CLASS (gobject_class),
674             G_SIGNAL_RUN_LAST,
675             G_STRUCT_OFFSET (IBusEngineClass, property_activate),
676             NULL, NULL,
677             _ibus_marshal_VOID__STRING_UINT,
678             G_TYPE_NONE,
679             2,
680             G_TYPE_STRING,
681             G_TYPE_UINT);
682 
683     /**
684      * IBusEngine::property-show:
685      * @engine: An IBusEngine.
686      * @name:   Property name.
687      *
688      * Emitted when a property is shown.
689      * Implement the member function IBusEngineClass::property_side
690      * in extended class to receive this signal.
691      *
692      * <note><para>Argument @user_data is ignored in this function.</para></note>
693      */
694     engine_signals[PROPERTY_SHOW] =
695         g_signal_new (I_("property-show"),
696             G_TYPE_FROM_CLASS (gobject_class),
697             G_SIGNAL_RUN_LAST,
698             G_STRUCT_OFFSET (IBusEngineClass, property_show),
699             NULL, NULL,
700             _ibus_marshal_VOID__STRING,
701             G_TYPE_NONE,
702             1,
703             G_TYPE_STRING);
704 
705     /**
706      * IBusEngine::property-hide:
707      * @engine: An IBusEngine.
708      * @name:   Property name.
709      *
710      * Emitted when a property is hidden.
711      * Implement the member function IBusEngineClass::property_hide
712      * in extended class to receive this signal.
713      *
714      * <note><para>Argument @user_data is ignored in this function.</para></note>
715      */
716     engine_signals[PROPERTY_HIDE] =
717         g_signal_new (I_("property-hide"),
718             G_TYPE_FROM_CLASS (gobject_class),
719             G_SIGNAL_RUN_LAST,
720             G_STRUCT_OFFSET (IBusEngineClass, property_hide),
721             NULL, NULL,
722             _ibus_marshal_VOID__STRING,
723             G_TYPE_NONE,
724             1,
725             G_TYPE_STRING);
726 
727     /**
728      * IBusEngine::process-hand-writing-event:
729      * @engine: An IBusEngine.
730      * @coordinates: An array of double (0.0 to 1.0) which represents a stroke (i.e. [x1, y1, x2, y2, x3, y3, ...]).
731      * @coordinates_len: The number of elements in the array.
732      *
733      * Emitted when a hand writing operation is cancelled.
734      * Implement the member function IBusEngineClass::cancel_hand_writing
735      * in extended class to receive this signal.
736      *
737      * <note><para>Argument @user_data is ignored in this function.</para></note>
738      */
739     engine_signals[PROCESS_HAND_WRITING_EVENT] =
740         g_signal_new (I_("process-hand-writing-event"),
741             G_TYPE_FROM_CLASS (gobject_class),
742             G_SIGNAL_RUN_LAST,
743             G_STRUCT_OFFSET (IBusEngineClass, process_hand_writing_event),
744             NULL, NULL,
745             _ibus_marshal_VOID__POINTER_UINT,
746             G_TYPE_NONE,
747             2,
748             G_TYPE_POINTER,
749             G_TYPE_UINT);
750 
751     /**
752      * IBusEngine::cancel-hand-writing:
753      * @engine: An IBusEngine.
754      * @n_strokes: The number of strokes to be removed. 0 means "remove all".
755      *
756      * Emitted when a hand writing operation is cancelled.
757      * Implement the member function IBusEngineClass::cancel_hand_writing
758      * in extended class to receive this signal.
759      *
760      * <note><para>Argument @user_data is ignored in this function.</para></note>
761      */
762     engine_signals[CANCEL_HAND_WRITING] =
763         g_signal_new (I_("cancel-hand-writing"),
764             G_TYPE_FROM_CLASS (gobject_class),
765             G_SIGNAL_RUN_LAST,
766             G_STRUCT_OFFSET (IBusEngineClass, cancel_hand_writing),
767             NULL, NULL,
768             _ibus_marshal_VOID__UINT,
769             G_TYPE_NONE,
770             1,
771             G_TYPE_UINT);
772 
773     /**
774      * IBusEngine::set-surrounding-text:
775      * @engine: An IBusEngine.
776      * @text: The surrounding text.
777      * @cursor_pos: The cursor position on surrounding text.
778      * @anchor_pos: The anchor position on selection area.
779      *
780      * Emitted when a surrounding text is set.
781      * Implement the member function IBusEngineClass::set_surrounding_text
782      * in extended class to receive this signal.
783      * If anchor_pos equals to cursor_pos, it means "there are no selection"
784      * or "does not support selection retrival".
785      *
786      * <note><para>Argument @user_data is ignored in this function.</para></note>
787      */
788     engine_signals[SET_SURROUNDING_TEXT] =
789         g_signal_new (I_("set-surrounding-text"),
790             G_TYPE_FROM_CLASS (gobject_class),
791             G_SIGNAL_RUN_LAST,
792             G_STRUCT_OFFSET (IBusEngineClass, set_surrounding_text),
793             NULL, NULL,
794             _ibus_marshal_VOID__OBJECT_UINT_UINT,
795             G_TYPE_NONE,
796             3,
797             G_TYPE_OBJECT,
798             G_TYPE_UINT,
799             G_TYPE_UINT);
800 
801     /**
802      * IBusEngine::set-content-type:
803      * @engine: An #IBusEngine.
804      * @purpose: Primary purpose of the input context, as an #IBusInputPurpose.
805      * @hints: Hints that augment @purpose, as an #IBusInputHints.
806      *
807      * Emitted when the client application content-type (primary
808      * purpose and hints) is set.  The engine could change the
809      * behavior according to the content-type.  Implement the member
810      * function IBusEngineClass::set_content_type
811      * in extended class to receive this signal.
812      *
813      * For example, if the client application wants to restrict input
814      * to numbers, this signal will be emitted with @purpose set to
815      * #IBUS_INPUT_PURPOSE_NUMBER, so the engine can switch the input
816      * mode to latin.
817      *
818      * <note><para>Argument @user_data is ignored in this
819      * function.</para></note>
820      */
821     engine_signals[SET_CONTENT_TYPE] =
822         g_signal_new (I_("set-content-type"),
823             G_TYPE_FROM_CLASS (gobject_class),
824             G_SIGNAL_RUN_LAST,
825             G_STRUCT_OFFSET (IBusEngineClass, set_content_type),
826             NULL, NULL,
827             _ibus_marshal_VOID__UINT_UINT,
828             G_TYPE_NONE,
829             2,
830             G_TYPE_UINT,
831             G_TYPE_UINT);
832 
833     text_empty = ibus_text_new_from_static_string ("");
834     g_object_ref_sink (text_empty);
835 }
836 
837 static void
ibus_engine_init(IBusEngine * engine)838 ibus_engine_init (IBusEngine *engine)
839 {
840     IBusEnginePrivate *priv;
841     engine->priv = priv = IBUS_ENGINE_GET_PRIVATE (engine);
842     priv->surrounding_text = g_object_ref_sink (text_empty);
843     priv->extension_keybindings = g_hash_table_new_full (
844             g_str_hash,
845             g_str_equal,
846             g_free,
847             g_free);
848 }
849 
850 static void
ibus_engine_destroy(IBusEngine * engine)851 ibus_engine_destroy (IBusEngine *engine)
852 {
853     IBusEnginePrivate *priv = engine->priv;
854 
855     g_clear_pointer (&priv->engine_name, g_free);
856     g_clear_pointer (&priv->current_extension_name, g_free);
857     if (priv->surrounding_text)
858         g_clear_object (&priv->surrounding_text);
859     if (priv->extension_keybindings)
860         g_clear_pointer (&priv->extension_keybindings, g_hash_table_destroy);
861 
862     IBUS_OBJECT_CLASS(ibus_engine_parent_class)->destroy (IBUS_OBJECT (engine));
863 }
864 
865 static void
ibus_engine_set_property(IBusEngine * engine,guint prop_id,const GValue * value,GParamSpec * pspec)866 ibus_engine_set_property (IBusEngine   *engine,
867                           guint         prop_id,
868                           const GValue *value,
869                           GParamSpec   *pspec)
870 {
871     switch (prop_id) {
872     case PROP_ENGINE_NAME:
873         engine->priv->engine_name = g_value_dup_string (value);
874         break;
875     default:
876         G_OBJECT_WARN_INVALID_PROPERTY_ID (engine, prop_id, pspec);
877     }
878 }
879 
880 static void
ibus_engine_get_property(IBusEngine * engine,guint prop_id,GValue * value,GParamSpec * pspec)881 ibus_engine_get_property (IBusEngine *engine,
882                           guint       prop_id,
883                           GValue     *value,
884                           GParamSpec *pspec)
885 {
886     switch (prop_id) {
887     case PROP_ENGINE_NAME:
888         g_value_set_string (value, engine->priv->engine_name);
889         break;
890 
891     default:
892         G_OBJECT_WARN_INVALID_PROPERTY_ID (engine, prop_id, pspec);
893     }
894 }
895 
896 static void
ibus_engine_panel_extension(IBusEngine * engine,const gchar * name)897 ibus_engine_panel_extension (IBusEngine  *engine,
898                              const gchar *name)
899 {
900     IBusEnginePrivate *priv;
901     IBusExtensionEvent *event;
902     GVariant *data;
903 
904     g_assert (IBUS_IS_ENGINE (engine));
905     g_assert (name);
906 
907     priv = engine->priv;
908     if (!g_strcmp0 (name, priv->current_extension_name))
909         priv->enable_extension = !priv->enable_extension;
910     else
911         priv->enable_extension = TRUE;
912     if (priv->enable_extension) {
913         g_free (priv->current_extension_name);
914         priv->current_extension_name = g_strdup (name);
915     }
916     event = ibus_extension_event_new (
917             "name", name,
918             "is-enabled", priv->enable_extension,
919             NULL);
920     g_assert (IBUS_IS_EXTENSION_EVENT (event));
921     data = ibus_serializable_serialize_object (
922             IBUS_SERIALIZABLE (event));
923 
924     g_assert (data != NULL);
925     ibus_engine_emit_signal (engine,
926                              "PanelExtension",
927                              g_variant_new ("(v)", data));
928     g_object_unref (event);
929 }
930 
931 static gboolean
ibus_engine_filter_key_event(IBusEngine * engine,guint keyval,guint keycode,guint state)932 ibus_engine_filter_key_event (IBusEngine *engine,
933                               guint       keyval,
934                               guint       keycode,
935                               guint       state)
936 {
937     IBusEnginePrivate *priv;
938     GList *names, *n;
939     IBusProcessKeyEventData *keys;
940     guint modifiers;
941 
942     if ((state & IBUS_RELEASE_MASK) != 0)
943         return FALSE;
944     g_return_val_if_fail (IBUS_IS_ENGINE (engine), FALSE);
945 
946     priv = engine->priv;
947     modifiers = state & IBUS_MODIFIER_FILTER;
948     if (keyval >= IBUS_KEY_A && keyval <= IBUS_KEY_Z &&
949         (modifiers & IBUS_SHIFT_MASK) != 0) {
950         keyval = keyval - IBUS_KEY_A + IBUS_KEY_a;
951     }
952     names = g_hash_table_get_keys (priv->extension_keybindings);
953     if (!names)
954         return FALSE;
955     for (n = names; n; n = n->next) {
956         const gchar *name = (const gchar *)n->data;
957         keys = g_hash_table_lookup (priv->extension_keybindings, name);
958         for (; keys; keys++) {
959             if (keys->keyval == 0 && keys->keycode == 0 && keys->state == 0)
960                 break;
961             if (keys->keyval == keyval &&
962                 keys->state == modifiers &&
963                 (keys->keycode == 0 || keys->keycode == keycode)) {
964                 ibus_engine_panel_extension (engine, name);
965                 return TRUE;
966             }
967         }
968     }
969     g_list_free (names);
970     return FALSE;
971 }
972 
973 static gboolean
ibus_engine_service_authorized_method(IBusService * service,GDBusConnection * connection)974 ibus_engine_service_authorized_method (IBusService     *service,
975                                        GDBusConnection *connection)
976 {
977     if (ibus_service_get_connection (service) == connection)
978         return TRUE;
979     return FALSE;
980 }
981 
982 static void
ibus_engine_service_panel_extension_register_keys(IBusEngine * engine,GVariant * parameters,GDBusMethodInvocation * invocation)983 ibus_engine_service_panel_extension_register_keys (IBusEngine      *engine,
984                                                    GVariant        *parameters,
985                                                    GDBusMethodInvocation
986                                                                    *invocation)
987 {
988     IBusEnginePrivate *priv = engine->priv;
989     GVariant *v1 = NULL;
990     GVariant *v2 = NULL;
991     GVariant *v3 = NULL;
992     GVariant *vkeys = NULL;
993     GVariantIter *iter1 = NULL;
994     GVariantIter *iter2 = NULL;
995     const gchar *name = NULL;
996     guint failure_id = 0;
997 
998     g_variant_get (parameters, "(v)", &v1);
999     if (v1)
1000         g_variant_get (v1, "(v)", &v2);
1001     else
1002         failure_id = 1;
1003     if (v2)
1004         g_variant_get (v2, "a{sv}", &iter1);
1005     else
1006         failure_id = 2;
1007     if (iter1) {
1008         while (g_variant_iter_loop (iter1, "{&sv}", &name, &vkeys)) {
1009             if (vkeys)
1010                 g_variant_get (vkeys, "av", &iter2);
1011             if (name && iter2) {
1012                 IBusProcessKeyEventData *keys = NULL;
1013                 gint num = 0;
1014                 while (g_variant_iter_loop (iter2, "v", &v3)) {
1015                     if (v3) {
1016                         guint keyval = 0;
1017                         guint keycode = 0;
1018                         guint state = 0;
1019                         g_variant_get (v3, "(iii)",
1020                                        &keyval, &keycode, &state);
1021                         if (!keys)
1022                             keys = g_new0 (IBusProcessKeyEventData, 2);
1023                         else
1024                             keys = g_renew (IBusProcessKeyEventData,
1025                                             keys,
1026                                             num + 2);
1027                         keys[num].keyval = keyval;
1028                         keys[num].keycode = keycode;
1029                         keys[num].state = state;
1030                         keys[num + 1].keyval = 0;
1031                         keys[num + 1].keycode = 0;
1032                         keys[num + 1].state = 0;
1033                         g_clear_pointer (&v3, g_variant_unref);
1034                         num++;
1035                     } else {
1036                         failure_id = 5;
1037                     }
1038                 }
1039                 if (num > 0) {
1040                     g_hash_table_replace (priv->extension_keybindings,
1041                                           g_strdup (name),
1042                                           keys);
1043                 } else {
1044                     g_hash_table_remove (priv->extension_keybindings, name);
1045                 }
1046                 g_clear_pointer (&iter2, g_variant_iter_free);
1047             } else {
1048                 failure_id = 4;
1049             }
1050             g_clear_pointer (&vkeys, g_variant_unref);
1051             name = NULL;
1052         }
1053         g_variant_iter_free (iter1);
1054     } else {
1055         failure_id = 3;
1056     }
1057     if (failure_id == 0) {
1058         g_dbus_method_invocation_return_value (invocation, NULL);
1059     } else {
1060         g_dbus_method_invocation_return_error (
1061                 invocation,
1062                 G_DBUS_ERROR,
1063                 G_DBUS_ERROR_FAILED,
1064                 "PanelExtensionRegisterKeys method gives NULL: %d",
1065                 failure_id);
1066     }
1067     if (v2)
1068         g_variant_unref (v2);
1069     if (v1)
1070         g_variant_unref (v1);
1071 }
1072 
1073 static void
ibus_engine_service_method_call(IBusService * service,GDBusConnection * connection,const gchar * sender,const gchar * object_path,const gchar * interface_name,const gchar * method_name,GVariant * parameters,GDBusMethodInvocation * invocation)1074 ibus_engine_service_method_call (IBusService           *service,
1075                                  GDBusConnection       *connection,
1076                                  const gchar           *sender,
1077                                  const gchar           *object_path,
1078                                  const gchar           *interface_name,
1079                                  const gchar           *method_name,
1080                                  GVariant              *parameters,
1081                                  GDBusMethodInvocation *invocation)
1082 {
1083     IBusEngine *engine = IBUS_ENGINE (service);
1084     IBusEnginePrivate *priv = engine->priv;
1085 
1086     if (g_strcmp0 (interface_name, IBUS_INTERFACE_ENGINE) != 0) {
1087         IBUS_SERVICE_CLASS (ibus_engine_parent_class)->
1088                 service_method_call (service,
1089                                      connection,
1090                                      sender,
1091                                      object_path,
1092                                      interface_name,
1093                                      method_name,
1094                                      parameters,
1095                                      invocation);
1096         return;
1097     }
1098 
1099     if (!ibus_engine_service_authorized_method (service, connection))
1100         return;
1101 
1102     if (g_strcmp0 (method_name, "ProcessKeyEvent") == 0) {
1103         guint keyval, keycode, state;
1104         gboolean retval = FALSE;
1105 
1106         g_variant_get (parameters, "(uuu)", &keyval, &keycode, &state);
1107         g_signal_emit (engine,
1108                        engine_signals[PROCESS_KEY_EVENT],
1109                        0,
1110                        keyval,
1111                        keycode,
1112                        state,
1113                        &retval);
1114         if (!retval) {
1115             retval = ibus_engine_filter_key_event (engine,
1116                                                    keyval,
1117                                                    keycode,
1118                                                    state);
1119         }
1120         g_dbus_method_invocation_return_value (invocation, g_variant_new ("(b)", retval));
1121         return;
1122     }
1123     if (g_strcmp0 (method_name, "PanelExtensionReceived") == 0) {
1124         GVariant *arg0 = NULL;
1125         IBusExtensionEvent *event = NULL;
1126 
1127         g_variant_get (parameters, "(v)", &arg0);
1128         if (arg0) {
1129             event = (IBusExtensionEvent *)ibus_serializable_deserialize_object (
1130                     arg0);
1131         }
1132         if (!event) {
1133             g_dbus_method_invocation_return_error (
1134                     invocation,
1135                     G_DBUS_ERROR,
1136                     G_DBUS_ERROR_FAILED,
1137                     "PanelExtensionReceived method gives NULL");
1138             return;
1139         }
1140         priv->enable_extension = ibus_extension_event_is_enabled (event);
1141         g_dbus_method_invocation_return_value (invocation, NULL);
1142         return;
1143     }
1144     if (g_strcmp0 (method_name, "PanelExtensionRegisterKeys") == 0) {
1145         ibus_engine_service_panel_extension_register_keys (engine,
1146                                                            parameters,
1147                                                            invocation);
1148         return;
1149     }
1150 
1151     static const struct {
1152         gchar *member;
1153         guint  signal_id;
1154     } no_arg_methods[] = {
1155         { "FocusIn",     FOCUS_IN },
1156         { "FocusOut",    FOCUS_OUT },
1157         { "Reset",       RESET },
1158         { "Enable",      ENABLE },
1159         { "Disable",     DISABLE },
1160         { "PageUp",      PAGE_UP },
1161         { "PageDown",    PAGE_DOWN },
1162         { "CursorUp",    CURSOR_UP },
1163         { "CursorDown",  CURSOR_DOWN },
1164     };
1165 
1166     gint i;
1167     for (i = 0; i < G_N_ELEMENTS (no_arg_methods); i++) {
1168         if (g_strcmp0 (method_name, no_arg_methods[i].member) == 0) {
1169             g_signal_emit (engine, engine_signals[no_arg_methods[i].signal_id], 0);
1170             g_dbus_method_invocation_return_value (invocation, NULL);
1171             return;
1172         }
1173     }
1174 
1175     if (g_strcmp0 (method_name, "CandidateClicked") == 0) {
1176         guint index, button, state;
1177         g_variant_get (parameters, "(uuu)", &index, &button, &state);
1178         g_signal_emit (engine,
1179                        engine_signals[CANDIDATE_CLICKED],
1180                        0,
1181                        index,
1182                        button,
1183                        state);
1184         g_dbus_method_invocation_return_value (invocation, NULL);
1185         return;
1186     }
1187 
1188     if (g_strcmp0 (method_name, "PropertyActivate") == 0) {
1189         gchar *name;
1190         guint state;
1191         g_variant_get (parameters, "(&su)", &name, &state);
1192         g_signal_emit (engine,
1193                        engine_signals[PROPERTY_ACTIVATE],
1194                        0,
1195                        name,
1196                        state);
1197         g_dbus_method_invocation_return_value (invocation, NULL);
1198         return;
1199     }
1200 
1201     if (g_strcmp0 (method_name, "PropertyShow") == 0) {
1202         gchar *name;
1203         g_variant_get (parameters, "(&s)", &name);
1204         g_signal_emit (engine,
1205                        engine_signals[PROPERTY_SHOW],
1206                        0,
1207                        name);
1208         g_dbus_method_invocation_return_value (invocation, NULL);
1209         return;
1210     }
1211 
1212     if (g_strcmp0 (method_name, "PropertyHide") == 0) {
1213         gchar *name;
1214         g_variant_get (parameters, "(&s)", &name);
1215         g_signal_emit (engine,
1216                        engine_signals[PROPERTY_HIDE],
1217                        0,
1218                        name);
1219         g_dbus_method_invocation_return_value (invocation, NULL);
1220         return;
1221     }
1222 
1223     if (g_strcmp0 (method_name, "SetCursorLocation") == 0) {
1224         gint x, y, w, h;
1225         g_variant_get (parameters, "(iiii)", &x, &y, &w, &h);
1226         engine->cursor_area.x = x;
1227         engine->cursor_area.y = y;
1228         engine->cursor_area.width = w;
1229         engine->cursor_area.height = h;
1230 
1231         g_signal_emit (engine,
1232                        engine_signals[SET_CURSOR_LOCATION],
1233                        0,
1234                        x, y, w, h);
1235         g_dbus_method_invocation_return_value (invocation, NULL);
1236         return;
1237     }
1238 
1239     if (g_strcmp0 (method_name, "SetCapabilities") == 0) {
1240         guint caps;
1241         g_variant_get (parameters, "(u)", &caps);
1242         engine->client_capabilities = caps;
1243         g_signal_emit (engine, engine_signals[SET_CAPABILITIES], 0, caps);
1244         g_dbus_method_invocation_return_value (invocation, NULL);
1245         return;
1246     }
1247 
1248     if (g_strcmp0 (method_name, "SetSurroundingText") == 0) {
1249         GVariant *variant = NULL;
1250         IBusText *text;
1251         guint cursor_pos;
1252         guint anchor_pos;
1253 
1254         g_variant_get (parameters,
1255                        "(vuu)",
1256                        &variant,
1257                        &cursor_pos,
1258                        &anchor_pos);
1259         text = IBUS_TEXT (ibus_serializable_deserialize (variant));
1260         g_variant_unref (variant);
1261 
1262         g_signal_emit (engine, engine_signals[SET_SURROUNDING_TEXT],
1263                        0,
1264                        text,
1265                        cursor_pos,
1266                        anchor_pos);
1267         if (g_object_is_floating (text)) {
1268             g_object_unref (text);
1269         }
1270         g_dbus_method_invocation_return_value (invocation, NULL);
1271         return;
1272     }
1273 
1274     if (g_strcmp0 (method_name, "ProcessHandWritingEvent") == 0) {
1275         const gdouble *coordinates;
1276         gsize coordinates_len = 0;
1277 
1278         coordinates = g_variant_get_fixed_array (g_variant_get_child_value (parameters, 0), &coordinates_len, sizeof (gdouble));
1279         g_return_if_fail (coordinates != NULL);
1280         g_return_if_fail (coordinates_len >= 4); /* The array should contain at least one line. */
1281         g_return_if_fail (coordinates_len <= G_MAXUINT); /* to prevent overflow in the cast in g_signal_emit */
1282         g_return_if_fail ((coordinates_len & 1) == 0);
1283 
1284         g_signal_emit (engine, engine_signals[PROCESS_HAND_WRITING_EVENT], 0,
1285                        coordinates, (guint) coordinates_len);
1286         g_dbus_method_invocation_return_value (invocation, NULL);
1287         return;
1288     }
1289 
1290     if (g_strcmp0 (method_name, "CancelHandWriting") == 0) {
1291         guint n_strokes = 0;
1292         g_variant_get (parameters, "(u)", &n_strokes);
1293         g_signal_emit (engine, engine_signals[CANCEL_HAND_WRITING], 0, n_strokes);
1294         g_dbus_method_invocation_return_value (invocation, NULL);
1295         return;
1296     }
1297 
1298     /* should not be reached */
1299     g_return_if_reached ();
1300 }
1301 
1302 static GVariant *
ibus_engine_service_get_property(IBusService * service,GDBusConnection * connection,const gchar * sender,const gchar * object_path,const gchar * interface_name,const gchar * property_name,GError ** error)1303 ibus_engine_service_get_property (IBusService        *service,
1304                                   GDBusConnection    *connection,
1305                                   const gchar        *sender,
1306                                   const gchar        *object_path,
1307                                   const gchar        *interface_name,
1308                                   const gchar        *property_name,
1309                                   GError            **error)
1310 {
1311     return IBUS_SERVICE_CLASS (ibus_engine_parent_class)->
1312                 service_get_property (service,
1313                                       connection,
1314                                       sender,
1315                                       object_path,
1316                                       interface_name,
1317                                       property_name,
1318                                       error);
1319 }
1320 
1321 static gboolean
ibus_engine_service_set_property(IBusService * service,GDBusConnection * connection,const gchar * sender,const gchar * object_path,const gchar * interface_name,const gchar * property_name,GVariant * value,GError ** error)1322 ibus_engine_service_set_property (IBusService        *service,
1323                                   GDBusConnection    *connection,
1324                                   const gchar        *sender,
1325                                   const gchar        *object_path,
1326                                   const gchar        *interface_name,
1327                                   const gchar        *property_name,
1328                                   GVariant           *value,
1329                                   GError            **error)
1330 {
1331     IBusEngine *engine = IBUS_ENGINE (service);
1332 
1333     if (g_strcmp0 (interface_name, IBUS_INTERFACE_ENGINE) != 0) {
1334         return IBUS_SERVICE_CLASS (ibus_engine_parent_class)->
1335             service_set_property (service,
1336                                   connection,
1337                                   sender,
1338                                   object_path,
1339                                   interface_name,
1340                                   property_name,
1341                                   value,
1342                                   error);
1343     }
1344 
1345     if (!ibus_engine_service_authorized_method (service, connection))
1346         return FALSE;
1347 
1348     if (g_strcmp0 (property_name, "ContentType") == 0) {
1349         guint purpose = 0;
1350         guint hints = 0;
1351 
1352         g_variant_get (value, "(uu)", &purpose, &hints);
1353         if (purpose != engine->priv->content_purpose ||
1354             hints != engine->priv->content_hints) {
1355             engine->priv->content_purpose = purpose;
1356             engine->priv->content_hints = hints;
1357 
1358             g_signal_emit (engine,
1359                            engine_signals[SET_CONTENT_TYPE],
1360                            0,
1361                            purpose,
1362                            hints);
1363 
1364             ibus_engine_dbus_property_changed (engine, "ContentType", value);
1365         }
1366 
1367         return TRUE;
1368     }
1369 
1370     g_return_val_if_reached (FALSE);
1371 }
1372 
1373 static gboolean
ibus_engine_process_key_event(IBusEngine * engine,guint keyval,guint keycode,guint state)1374 ibus_engine_process_key_event (IBusEngine *engine,
1375                                guint       keyval,
1376                                guint       keycode,
1377                                guint       state)
1378 {
1379     return FALSE;
1380 }
1381 
1382 static void
ibus_engine_focus_in(IBusEngine * engine)1383 ibus_engine_focus_in (IBusEngine *engine)
1384 {
1385     // g_debug ("focus-in");
1386 }
1387 
1388 static void
ibus_engine_focus_out(IBusEngine * engine)1389 ibus_engine_focus_out (IBusEngine *engine)
1390 {
1391     // g_debug ("focus-out");
1392 }
1393 
1394 static void
ibus_engine_reset(IBusEngine * engine)1395 ibus_engine_reset (IBusEngine *engine)
1396 {
1397     // g_debug ("reset");
1398 }
1399 
1400 static void
ibus_engine_enable(IBusEngine * engine)1401 ibus_engine_enable (IBusEngine *engine)
1402 {
1403     // g_debug ("enable");
1404 }
1405 
1406 static void
ibus_engine_disable(IBusEngine * engine)1407 ibus_engine_disable (IBusEngine *engine)
1408 {
1409     // g_debug ("disable");
1410 }
1411 
1412 static void
ibus_engine_set_cursor_location(IBusEngine * engine,gint x,gint y,gint w,gint h)1413 ibus_engine_set_cursor_location (IBusEngine *engine,
1414                                  gint        x,
1415                                  gint        y,
1416                                  gint        w,
1417                                  gint        h)
1418 {
1419     // g_debug ("set-cursor-location (%d, %d, %d, %d)", x, y, w, h);
1420 }
1421 
1422 static void
ibus_engine_set_capabilities(IBusEngine * engine,guint caps)1423 ibus_engine_set_capabilities (IBusEngine *engine,
1424                               guint       caps)
1425 {
1426     // g_debug ("set-capabilities (0x%04x)", caps);
1427 }
1428 
1429 static void
ibus_engine_page_up(IBusEngine * engine)1430 ibus_engine_page_up (IBusEngine *engine)
1431 {
1432     // g_debug ("page-up");
1433 }
1434 
1435 static void
ibus_engine_page_down(IBusEngine * engine)1436 ibus_engine_page_down (IBusEngine *engine)
1437 {
1438     // g_debug ("page-down");
1439 }
1440 
1441 static void
ibus_engine_cursor_up(IBusEngine * engine)1442 ibus_engine_cursor_up (IBusEngine *engine)
1443 {
1444     // g_debug ("cursor-up");
1445 }
1446 
1447 static void
ibus_engine_cursor_down(IBusEngine * engine)1448 ibus_engine_cursor_down (IBusEngine *engine)
1449 {
1450     // g_debug ("cursor-down");
1451 }
1452 
1453 static void
ibus_engine_candidate_clicked(IBusEngine * engine,guint index,guint button,guint state)1454 ibus_engine_candidate_clicked (IBusEngine *engine,
1455                                guint       index,
1456                                guint       button,
1457                                guint       state)
1458 {
1459     // g_debug ("candidate-clicked");
1460 }
1461 
1462 static void
ibus_engine_property_activate(IBusEngine * engine,const gchar * prop_name,guint prop_state)1463 ibus_engine_property_activate (IBusEngine  *engine,
1464                                const gchar *prop_name,
1465                                guint        prop_state)
1466 {
1467     // g_debug ("property-activate ('%s', %d)", prop_name, prop_state);
1468 }
1469 
1470 static void
ibus_engine_property_show(IBusEngine * engine,const gchar * prop_name)1471 ibus_engine_property_show (IBusEngine *engine, const gchar *prop_name)
1472 {
1473     // g_debug ("property-show ('%s')", prop_name);
1474 }
1475 
1476 static void
ibus_engine_property_hide(IBusEngine * engine,const gchar * prop_name)1477 ibus_engine_property_hide (IBusEngine *engine, const gchar *prop_name)
1478 {
1479     // g_debug ("property-hide ('%s')", prop_name);
1480 }
1481 
1482 static void
ibus_engine_set_surrounding_text(IBusEngine * engine,IBusText * text,guint cursor_pos,guint anchor_pos)1483 ibus_engine_set_surrounding_text (IBusEngine *engine,
1484                                   IBusText   *text,
1485                                   guint       cursor_pos,
1486                                   guint       anchor_pos)
1487 {
1488     g_assert (IBUS_IS_ENGINE (engine));
1489 
1490     if (engine->priv->surrounding_text) {
1491         g_object_unref (engine->priv->surrounding_text);
1492     }
1493 
1494     engine->priv->surrounding_text = (IBusText *) g_object_ref_sink (text ? text : text_empty);
1495     engine->priv->surrounding_cursor_pos = cursor_pos;
1496     engine->priv->selection_anchor_pos = anchor_pos;
1497     // g_debug ("set-surrounding-text ('%s', %d, %d)", text->text, cursor_pos, anchor_pos);
1498 }
1499 
1500 static void
ibus_engine_process_hand_writing_event(IBusEngine * engine,const gdouble * coordinates,guint coordinates_len)1501 ibus_engine_process_hand_writing_event (IBusEngine         *engine,
1502                                         const gdouble      *coordinates,
1503                                         guint               coordinates_len)
1504 {
1505     // guint i;
1506     // g_debug ("process-hand-writing-event (%u)", coordinates_len);
1507     // for (i = 0; i < coordinates_len; i++)
1508     //     g_debug (" %lf", coordinates[i]);
1509 }
1510 
1511 static void
ibus_engine_cancel_hand_writing(IBusEngine * engine,guint n_strokes)1512 ibus_engine_cancel_hand_writing (IBusEngine         *engine,
1513                                  guint               n_strokes)
1514 {
1515     // g_debug ("cancel-hand-writing (%u)", n_strokes);
1516 }
1517 
1518 static void
ibus_engine_set_content_type(IBusEngine * engine,guint purpose,guint hints)1519 ibus_engine_set_content_type (IBusEngine *engine,
1520                               guint       purpose,
1521                               guint       hints)
1522 {
1523     // g_debug ("set-content-type (%u %u)", purpose, hints);
1524 }
1525 
1526 static void
ibus_engine_emit_signal(IBusEngine * engine,const gchar * signal_name,GVariant * parameters)1527 ibus_engine_emit_signal (IBusEngine  *engine,
1528                          const gchar *signal_name,
1529                          GVariant    *parameters)
1530 {
1531     ibus_service_emit_signal ((IBusService *)engine,
1532                               NULL,
1533                               IBUS_INTERFACE_ENGINE,
1534                               signal_name,
1535                               parameters,
1536                               NULL);
1537 }
1538 
1539 static void
ibus_engine_dbus_property_changed(IBusEngine * engine,const gchar * property_name,GVariant * value)1540 ibus_engine_dbus_property_changed (IBusEngine  *engine,
1541                                    const gchar *property_name,
1542                                    GVariant    *value)
1543 {
1544     const gchar *object_path;
1545     GDBusConnection *connection;
1546     GDBusMessage *message;
1547     GVariantBuilder *builder;
1548     gboolean retval;
1549     GError *error;
1550 
1551     /* we cannot use ibus_service_emit_signal() here, since we need to
1552        set sender of the signal so that GDBusProxy can properly track
1553        the property change. */
1554     object_path = ibus_service_get_object_path ((IBusService *)engine);
1555     message = g_dbus_message_new_signal (object_path,
1556                                          "org.freedesktop.DBus.Properties",
1557                                          "PropertiesChanged");
1558 
1559     g_dbus_message_set_sender (message, "org.freedesktop.IBus");
1560 
1561     builder = g_variant_builder_new (G_VARIANT_TYPE_ARRAY);
1562     g_variant_builder_add (builder, "{sv}", property_name, value);
1563     g_dbus_message_set_body (message,
1564                              g_variant_new ("(sa{sv}as)",
1565                                             IBUS_INTERFACE_ENGINE,
1566                                             builder,
1567                                             NULL));
1568     g_variant_builder_unref (builder);
1569 
1570     error = NULL;
1571     connection = ibus_service_get_connection ((IBusService *)engine);
1572     retval = g_dbus_connection_send_message (connection,
1573                                              message,
1574                                              G_DBUS_SEND_MESSAGE_FLAGS_NONE,
1575                                              NULL,
1576                                              &error);
1577     if (!retval) {
1578         g_warning ("Failed to emit PropertiesChanged signal: %s",
1579                    error->message);
1580         g_error_free (error);
1581     }
1582     g_object_unref (message);
1583 }
1584 
1585 IBusEngine *
ibus_engine_new(const gchar * engine_name,const gchar * object_path,GDBusConnection * connection)1586 ibus_engine_new (const gchar     *engine_name,
1587                  const gchar     *object_path,
1588                  GDBusConnection *connection)
1589 {
1590     return ibus_engine_new_with_type (IBUS_TYPE_ENGINE,
1591                                       engine_name,
1592                                       object_path,
1593                                       connection);
1594 }
1595 
1596 IBusEngine  *
ibus_engine_new_with_type(GType engine_type,const gchar * engine_name,const gchar * object_path,GDBusConnection * connection)1597 ibus_engine_new_with_type (GType            engine_type,
1598                            const gchar     *engine_name,
1599                            const gchar     *object_path,
1600                            GDBusConnection *connection)
1601 {
1602     g_return_val_if_fail (g_type_is_a (engine_type, IBUS_TYPE_ENGINE), NULL);
1603     g_return_val_if_fail (engine_name != NULL, NULL);
1604     g_return_val_if_fail (object_path != NULL, NULL);
1605     g_return_val_if_fail (G_IS_DBUS_CONNECTION (connection), NULL);
1606 
1607     GObject *object = g_object_new (engine_type,
1608                                     "engine-name", engine_name,
1609                                     "object-path", object_path,
1610                                     "connection", connection,
1611                                     NULL);
1612     return IBUS_ENGINE (object);
1613 }
1614 
1615 
1616 void
ibus_engine_commit_text(IBusEngine * engine,IBusText * text)1617 ibus_engine_commit_text (IBusEngine *engine,
1618                          IBusText   *text)
1619 {
1620     g_return_if_fail (IBUS_IS_ENGINE (engine));
1621     g_return_if_fail (IBUS_IS_TEXT (text));
1622 
1623     GVariant *variant = ibus_serializable_serialize ((IBusSerializable *)text);
1624     ibus_engine_emit_signal (engine,
1625                              "CommitText",
1626                              g_variant_new ("(v)", variant));
1627 
1628     if (g_object_is_floating (text)) {
1629         g_object_unref (text);
1630     }
1631 }
1632 
1633 void
ibus_engine_update_preedit_text(IBusEngine * engine,IBusText * text,guint cursor_pos,gboolean visible)1634 ibus_engine_update_preedit_text (IBusEngine      *engine,
1635                                  IBusText        *text,
1636                                  guint            cursor_pos,
1637                                  gboolean         visible)
1638 {
1639     ibus_engine_update_preedit_text_with_mode (engine,
1640             text, cursor_pos, visible, IBUS_ENGINE_PREEDIT_CLEAR);
1641 }
1642 
1643 void
ibus_engine_update_preedit_text_with_mode(IBusEngine * engine,IBusText * text,guint cursor_pos,gboolean visible,IBusPreeditFocusMode mode)1644 ibus_engine_update_preedit_text_with_mode (IBusEngine            *engine,
1645                                            IBusText              *text,
1646                                            guint                  cursor_pos,
1647                                            gboolean               visible,
1648                                            IBusPreeditFocusMode   mode)
1649 {
1650     g_return_if_fail (IBUS_IS_ENGINE (engine));
1651     g_return_if_fail (IBUS_IS_TEXT (text));
1652 
1653     GVariant *variant = ibus_serializable_serialize ((IBusSerializable *)text);
1654     ibus_engine_emit_signal (engine,
1655                              "UpdatePreeditText",
1656                              g_variant_new ("(vubu)", variant, cursor_pos, visible, mode));
1657 
1658     if (g_object_is_floating (text)) {
1659         g_object_unref (text);
1660     }
1661 }
1662 
ibus_engine_update_auxiliary_text(IBusEngine * engine,IBusText * text,gboolean visible)1663 void ibus_engine_update_auxiliary_text (IBusEngine      *engine,
1664                                         IBusText        *text,
1665                                         gboolean         visible)
1666 {
1667     g_return_if_fail (IBUS_IS_ENGINE (engine));
1668     g_return_if_fail (IBUS_IS_TEXT (text));
1669 
1670     GVariant *variant = ibus_serializable_serialize ((IBusSerializable *)text);
1671     ibus_engine_emit_signal (engine,
1672                              "UpdateAuxiliaryText",
1673                              g_variant_new ("(vb)", variant, visible));
1674 
1675     if (g_object_is_floating (text)) {
1676         g_object_unref (text);
1677     }
1678 }
1679 
1680 
1681 void
ibus_engine_update_lookup_table(IBusEngine * engine,IBusLookupTable * table,gboolean visible)1682 ibus_engine_update_lookup_table (IBusEngine        *engine,
1683                                  IBusLookupTable   *table,
1684                                  gboolean           visible)
1685 {
1686     g_return_if_fail (IBUS_IS_ENGINE (engine));
1687     g_return_if_fail (IBUS_IS_LOOKUP_TABLE (table));
1688 
1689     GVariant *variant = ibus_serializable_serialize ((IBusSerializable *)table);
1690     ibus_engine_emit_signal (engine,
1691                              "UpdateLookupTable",
1692                              g_variant_new ("(vb)", variant, visible));
1693 
1694     if (g_object_is_floating (table)) {
1695         g_object_unref (table);
1696     }
1697 }
1698 
1699 void
ibus_engine_update_lookup_table_fast(IBusEngine * engine,IBusLookupTable * table,gboolean visible)1700 ibus_engine_update_lookup_table_fast (IBusEngine        *engine,
1701                                       IBusLookupTable   *table,
1702                                       gboolean           visible)
1703 {
1704     /* Note: gnome shell needs the previous page and next page
1705        to correctly show the page up/down arrows,
1706        send three pages instead of one page. */
1707 
1708     g_return_if_fail (IBUS_IS_ENGINE (engine));
1709     g_return_if_fail (IBUS_IS_LOOKUP_TABLE (table));
1710 
1711     IBusLookupTable *new_table;
1712     IBusText *text;
1713     gint page_begin;
1714     gint cursor_pos;
1715     gint i;
1716 
1717     if (table->candidates->len < table->page_size << 2) {
1718         ibus_engine_update_lookup_table (engine, table, visible);
1719         return;
1720     }
1721 
1722     page_begin = (table->cursor_pos / table->page_size) * table->page_size;
1723     cursor_pos = ibus_lookup_table_get_cursor_in_page (table);
1724 
1725     if (table->cursor_pos >= table->page_size) {
1726         /* has previous page, adjust the value. */
1727         page_begin -= table->page_size;
1728         cursor_pos += table->page_size;
1729     }
1730 
1731     new_table = ibus_lookup_table_new
1732         (table->page_size, 0, table->cursor_visible, table->round);
1733 
1734     /* '3' means the previous page, current page and next page. */
1735     for (i = page_begin; i < page_begin + 3 * table->page_size &&
1736              i < table->candidates->len; i++) {
1737         ibus_lookup_table_append_candidate
1738             (new_table, ibus_lookup_table_get_candidate (table, i));
1739     }
1740 
1741     for (i = 0; (text = ibus_lookup_table_get_label (table, i)) != NULL; i++) {
1742         ibus_lookup_table_append_label (new_table, text);
1743     }
1744 
1745     ibus_lookup_table_set_cursor_pos (new_table, cursor_pos);
1746     ibus_lookup_table_set_orientation
1747         (new_table, ibus_lookup_table_get_orientation (table));
1748 
1749     ibus_engine_update_lookup_table (engine, new_table, visible);
1750 
1751     if (g_object_is_floating (table)) {
1752         g_object_unref (table);
1753     }
1754 }
1755 
1756 void
ibus_engine_forward_key_event(IBusEngine * engine,guint keyval,guint keycode,guint state)1757 ibus_engine_forward_key_event (IBusEngine      *engine,
1758                                guint            keyval,
1759                                guint            keycode,
1760                                guint            state)
1761 {
1762     g_return_if_fail (IBUS_IS_ENGINE (engine));
1763 
1764     ibus_engine_emit_signal (engine,
1765                              "ForwardKeyEvent",
1766                              g_variant_new ("(uuu)", keyval, keycode, state));
1767 }
1768 
ibus_engine_delete_surrounding_text(IBusEngine * engine,gint offset_from_cursor,guint nchars)1769 void ibus_engine_delete_surrounding_text (IBusEngine      *engine,
1770                                           gint             offset_from_cursor,
1771                                           guint            nchars)
1772 {
1773     IBusEnginePrivate *priv;
1774 
1775     g_return_if_fail (IBUS_IS_ENGINE (engine));
1776 
1777     priv = IBUS_ENGINE_GET_PRIVATE (engine);
1778 
1779     /* Update surrounding-text cache.  This is necessary since some
1780        engines call ibus_engine_get_surrounding_text() immediately
1781        after ibus_engine_delete_surrounding_text(). */
1782     if (priv->surrounding_text) {
1783         IBusText *text;
1784         glong cursor_pos, len;
1785 
1786         cursor_pos = priv->surrounding_cursor_pos + offset_from_cursor;
1787         len = ibus_text_get_length (priv->surrounding_text);
1788         if (cursor_pos >= 0 && len - cursor_pos >= nchars) {
1789             gunichar *ucs;
1790 
1791             ucs = g_utf8_to_ucs4_fast (priv->surrounding_text->text,
1792                                        -1,
1793                                        NULL);
1794             memmove (&ucs[cursor_pos],
1795                      &ucs[cursor_pos + nchars],
1796                      sizeof(gunichar) * (len - cursor_pos - nchars));
1797             ucs[len - nchars] = 0;
1798             text = ibus_text_new_from_ucs4 (ucs);
1799             g_free (ucs);
1800             priv->surrounding_cursor_pos = cursor_pos;
1801         } else {
1802             text = text_empty;
1803             priv->surrounding_cursor_pos = 0;
1804         }
1805 
1806         g_object_unref (priv->surrounding_text);
1807         priv->surrounding_text = g_object_ref_sink (text);
1808     }
1809 
1810     ibus_engine_emit_signal (engine,
1811                              "DeleteSurroundingText",
1812                              g_variant_new ("(iu)", offset_from_cursor, nchars));
1813 }
1814 
1815 void
ibus_engine_get_surrounding_text(IBusEngine * engine,IBusText ** text,guint * cursor_pos,guint * anchor_pos)1816 ibus_engine_get_surrounding_text (IBusEngine   *engine,
1817                                   IBusText    **text,
1818                                   guint        *cursor_pos,
1819                                   guint        *anchor_pos)
1820 {
1821     IBusEnginePrivate *priv;
1822 
1823     g_return_if_fail (IBUS_IS_ENGINE (engine));
1824     const gboolean signal_only = (text == NULL);
1825 
1826     g_return_if_fail (( signal_only && (cursor_pos == NULL)) ||
1827                       (!signal_only && (cursor_pos != NULL)));
1828 
1829     g_return_if_fail (( signal_only && (anchor_pos == NULL)) ||
1830                       (!signal_only && (anchor_pos != NULL)));
1831 
1832     priv = IBUS_ENGINE_GET_PRIVATE (engine);
1833 
1834     if (!signal_only) {
1835         *text = g_object_ref (priv->surrounding_text);
1836         *cursor_pos = priv->surrounding_cursor_pos;
1837         *anchor_pos = priv->selection_anchor_pos;
1838     }
1839 
1840     /* tell the client that this engine will utilize surrounding-text
1841      * feature, which causes periodical update.  Note that the client
1842      * should request the initial surrounding-text when the engine is
1843      * enabled (see ibus_im_context_focus_in() and
1844      * _ibus_context_enabled_cb() in client/gtk2/ibusimcontext.c). */
1845     ibus_engine_emit_signal (engine,
1846                              "RequireSurroundingText",
1847                              NULL);
1848 
1849     // g_debug ("get-surrounding-text ('%s', %d, %d)", (*text)->text, *cursor_pos, *anchor_pos);
1850 }
1851 
1852 void
ibus_engine_get_content_type(IBusEngine * engine,guint * purpose,guint * hints)1853 ibus_engine_get_content_type (IBusEngine *engine,
1854                               guint      *purpose,
1855                               guint      *hints)
1856 {
1857     g_return_if_fail (IBUS_IS_ENGINE (engine));
1858 
1859     *purpose = engine->priv->content_purpose;
1860     *hints = engine->priv->content_hints;
1861 }
1862 
1863 void
ibus_engine_register_properties(IBusEngine * engine,IBusPropList * prop_list)1864 ibus_engine_register_properties (IBusEngine   *engine,
1865                                  IBusPropList *prop_list)
1866 {
1867     g_return_if_fail (IBUS_IS_ENGINE (engine));
1868     g_return_if_fail (IBUS_IS_PROP_LIST (prop_list));
1869 
1870     GVariant *variant = ibus_serializable_serialize ((IBusSerializable *)prop_list);
1871     ibus_engine_emit_signal (engine,
1872                              "RegisterProperties",
1873                              g_variant_new ("(v)", variant));
1874 
1875     if (g_object_is_floating (prop_list)) {
1876         g_object_unref (prop_list);
1877     }
1878 }
1879 
1880 void
ibus_engine_update_property(IBusEngine * engine,IBusProperty * prop)1881 ibus_engine_update_property (IBusEngine   *engine,
1882                              IBusProperty *prop)
1883 {
1884     g_return_if_fail (IBUS_IS_ENGINE (engine));
1885     g_return_if_fail (IBUS_IS_PROPERTY (prop));
1886 
1887     GVariant *variant = ibus_serializable_serialize ((IBusSerializable *)prop);
1888     ibus_engine_emit_signal (engine,
1889                              "UpdateProperty",
1890                              g_variant_new ("(v)", variant));
1891 
1892     if (g_object_is_floating (prop)) {
1893         g_object_unref (prop);
1894     }
1895 }
1896 
1897 #define DEFINE_FUNC(name, Name)                             \
1898     void                                                    \
1899     ibus_engine_##name (IBusEngine *engine)                 \
1900     {                                                       \
1901         g_return_if_fail (IBUS_IS_ENGINE (engine));         \
1902         ibus_engine_emit_signal (engine,                    \
1903                               #Name,                        \
1904                               NULL);                        \
1905     }
DEFINE_FUNC(show_preedit_text,ShowPreeditText)1906 DEFINE_FUNC (show_preedit_text, ShowPreeditText)
1907 DEFINE_FUNC (hide_preedit_text, HidePreeditText)
1908 DEFINE_FUNC (show_auxiliary_text, ShowAuxiliaryText)
1909 DEFINE_FUNC (hide_auxiliary_text, HideAuxiliaryText)
1910 DEFINE_FUNC (show_lookup_table, ShowLookupTable)
1911 DEFINE_FUNC (hide_lookup_table, HideLookupTable)
1912 #undef DEFINE_FUNC
1913 
1914 const gchar *
1915 ibus_engine_get_name (IBusEngine *engine)
1916 {
1917     g_return_val_if_fail (IBUS_IS_ENGINE (engine), NULL);
1918     return engine->priv->engine_name;
1919 }
1920