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