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-2010 Peng Huang <shawn.p.huang@gmail.com>
5 * Copyright (C) 2008-2010 Red Hat, Inc.
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation; either
10 * version 2 of the License, or (at your option) any later version.
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with this library; if not, write to the
19 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
20 * Boston, MA 02111-1307, USA.
29 #include <gdk/gdkkeysyms.h>
31 #include "ibusimcontext.h"
33 #if !GTK_CHECK_VERSION (2, 91, 0)
34 # define DEPRECATED_GDK_KEYSYMS 1
38 # define IDEBUG g_debug
43 struct _IBusIMContext {
46 /* instance members */
48 GdkWindow *client_window;
52 IBusInputContext *ibuscontext;
55 gchar *preedit_string;
56 PangoAttrList *preedit_attrs;
57 gint preedit_cursor_pos;
58 gboolean preedit_visible;
60 GdkRectangle cursor_area;
67 GCancellable *cancellable;
70 struct _IBusIMContextClass {
71 GtkIMContextClass parent;
75 static guint _signal_commit_id = 0;
76 static guint _signal_preedit_changed_id = 0;
77 static guint _signal_preedit_start_id = 0;
78 static guint _signal_preedit_end_id = 0;
79 static guint _signal_delete_surrounding_id = 0;
80 static guint _signal_retrieve_surrounding_id = 0;
82 static const gchar *_no_snooper_apps = NO_SNOOPER_APPS;
83 static gboolean _use_key_snooper = ENABLE_SNOOPER;
84 static guint _key_snooper_id = 0;
86 static gboolean _use_sync_mode = FALSE;
88 static GtkIMContext *_focus_im_context = NULL;
89 static IBusInputContext *_fake_context = NULL;
90 static GdkWindow *_input_window = NULL;
91 static GtkWidget *_input_widget = NULL;
93 /* functions prototype */
94 static void ibus_im_context_class_init (IBusIMContextClass *class);
95 static void ibus_im_context_class_fini (IBusIMContextClass *class);
96 static void ibus_im_context_init (GObject *obj);
97 static void ibus_im_context_finalize (GObject *obj);
98 static void ibus_im_context_reset (GtkIMContext *context);
99 static gboolean ibus_im_context_filter_keypress
100 (GtkIMContext *context,
102 static void ibus_im_context_focus_in (GtkIMContext *context);
103 static void ibus_im_context_focus_out (GtkIMContext *context);
104 static void ibus_im_context_get_preedit_string
105 (GtkIMContext *context,
107 PangoAttrList **attrs,
109 static void ibus_im_context_set_client_window
110 (GtkIMContext *context,
112 static void ibus_im_context_set_cursor_location
113 (GtkIMContext *context,
115 static void ibus_im_context_set_use_preedit
116 (GtkIMContext *context,
117 gboolean use_preedit);
118 static void ibus_im_context_set_surrounding
119 (GtkIMContext *slave,
126 static void _create_input_context (IBusIMContext *context);
127 static gboolean _set_cursor_location_internal
128 (IBusIMContext *context);
130 static void _bus_connected_cb (IBusBus *bus,
131 IBusIMContext *context);
132 /* callback functions for slave context */
133 static void _slave_commit_cb (GtkIMContext *slave,
135 IBusIMContext *context);
136 static void _slave_preedit_changed_cb (GtkIMContext *slave,
137 IBusIMContext *context);
138 static void _slave_preedit_start_cb (GtkIMContext *slave,
139 IBusIMContext *context);
140 static void _slave_preedit_end_cb (GtkIMContext *slave,
141 IBusIMContext *context);
142 static gboolean _slave_retrieve_surrounding_cb
143 (GtkIMContext *slave,
144 IBusIMContext *context);
145 static gboolean _slave_delete_surrounding_cb
146 (GtkIMContext *slave,
147 gint offset_from_cursor,
149 IBusIMContext *context);
150 static void _request_surrounding_text (IBusIMContext *context,
152 static void _create_fake_input_context (void);
156 static GType _ibus_type_im_context = 0;
157 static GtkIMContextClass *parent_class = NULL;
159 static IBusBus *_bus = NULL;
162 ibus_im_context_register_type (GTypeModule *type_module)
164 IDEBUG ("%s", __FUNCTION__);
166 static const GTypeInfo ibus_im_context_info = {
167 sizeof (IBusIMContextClass),
168 (GBaseInitFunc) NULL,
169 (GBaseFinalizeFunc) NULL,
170 (GClassInitFunc) ibus_im_context_class_init,
171 (GClassFinalizeFunc) ibus_im_context_class_fini,
172 NULL, /* class data */
173 sizeof (IBusIMContext),
175 (GInstanceInitFunc) ibus_im_context_init,
178 if (!_ibus_type_im_context) {
180 _ibus_type_im_context =
181 g_type_module_register_type (type_module,
184 &ibus_im_context_info,
188 _ibus_type_im_context =
189 g_type_register_static (GTK_TYPE_IM_CONTEXT,
191 &ibus_im_context_info,
198 ibus_im_context_get_type (void)
200 IDEBUG ("%s", __FUNCTION__);
202 if (_ibus_type_im_context == 0) {
203 ibus_im_context_register_type (NULL);
206 g_assert (_ibus_type_im_context != 0);
207 return _ibus_type_im_context;
211 ibus_im_context_new (void)
213 IDEBUG ("%s", __FUNCTION__);
215 GObject *obj = g_object_new (IBUS_TYPE_IM_CONTEXT, NULL);
216 return IBUS_IM_CONTEXT (obj);
220 _focus_in_cb (GtkWidget *widget,
221 GdkEventFocus *event,
224 if (_focus_im_context == NULL && _fake_context != NULL) {
225 ibus_input_context_focus_in (_fake_context);
231 _focus_out_cb (GtkWidget *widget,
232 GdkEventFocus *event,
235 if (_focus_im_context == NULL && _fake_context != NULL) {
236 ibus_input_context_focus_out (_fake_context);
242 _process_key_event_done (GObject *object,
246 IBusInputContext *context = (IBusInputContext *)object;
247 GdkEventKey *event = (GdkEventKey *) user_data;
249 GError *error = NULL;
250 gboolean retval = ibus_input_context_process_key_event_async_finish (
256 g_warning ("Process Key Event failed: %s.", error->message);
257 g_error_free (error);
260 if (retval == FALSE) {
261 event->state |= IBUS_IGNORED_MASK;
262 gdk_event_put ((GdkEvent *)event);
264 gdk_event_free ((GdkEvent *)event);
268 /* emit "retrieve-surrounding" glib signal of GtkIMContext, if
269 * context->caps has IBUS_CAP_SURROUNDING_TEXT and the current IBus
270 * engine needs surrounding-text.
272 * if "force" is TRUE, emit the signal regardless of whether the
273 * engine needs surrounding-text.
276 _request_surrounding_text (IBusIMContext *context, gboolean force)
278 if (context && context->enable &&
279 (context->caps & IBUS_CAP_SURROUNDING_TEXT) != 0 &&
281 ibus_input_context_needs_surrounding_text (context->ibuscontext))) {
282 gboolean return_value;
283 IDEBUG ("requesting surrounding text");
284 g_signal_emit (context, _signal_retrieve_surrounding_id, 0,
291 _key_snooper_cb (GtkWidget *widget,
295 IDEBUG ("%s", __FUNCTION__);
296 gboolean retval = FALSE;
298 IBusIMContext *ibusimcontext = (IBusIMContext *) _focus_im_context;
299 IBusInputContext *ibuscontext = NULL;
301 if (ibusimcontext != NULL &&
302 ibusimcontext->has_focus == TRUE) {
303 /* has IC with focus and use_key_snooper is true */
304 if (_use_key_snooper)
305 ibuscontext = ibusimcontext->ibuscontext;
308 /* If no IC has focus, and fake IC has been created, then pass key events to fake IC. */
309 ibuscontext = _fake_context;
312 if (ibuscontext == NULL)
315 if (G_UNLIKELY (event->state & IBUS_HANDLED_MASK))
318 if (G_UNLIKELY (event->state & IBUS_IGNORED_MASK))
322 if (_fake_context != ibuscontext)
325 /* window has input focus is not changed */
326 if (_input_window == event->window)
329 if (_input_window != NULL) {
330 g_object_remove_weak_pointer ((GObject *) _input_window,
331 (gpointer *) &_input_window);
333 if (event->window != NULL) {
334 g_object_add_weak_pointer ((GObject *) event->window,
335 (gpointer *) &_input_window);
337 _input_window = event->window;
339 /* Trace widget has input focus, and listen focus events of it.
340 * It is workaround for Alt+Shift+Tab shortcut key issue(crosbug.com/8855).
341 * gtk_get_event_widget returns the widget that is associated with the
342 * GdkWindow of the GdkEvent.
344 GtkWidget *widget = gtk_get_event_widget ((GdkEvent *)event);
345 /* g_assert (_input_widget != widget). */
346 if (_input_widget == widget)
349 if (_input_widget != NULL) {
350 g_signal_handlers_disconnect_by_func (_input_widget,
351 (GCallback) _focus_in_cb,
353 g_signal_handlers_disconnect_by_func (_input_widget,
354 (GCallback) _focus_out_cb,
356 g_object_remove_weak_pointer ((GObject *) _input_widget,
357 (gpointer *) &_input_widget);
360 if (widget != NULL) {
361 g_signal_connect (widget,
363 (GCallback) _focus_in_cb,
365 g_signal_connect (widget,
367 (GCallback) _focus_out_cb,
369 g_object_add_weak_pointer ((GObject *) widget,
370 (gpointer *) &_input_widget);
372 _input_widget = widget;
376 if (ibusimcontext != NULL) {
377 _request_surrounding_text (ibusimcontext, FALSE);
378 ibusimcontext->time = event->time;
381 guint state = event->state;
382 if (event->type == GDK_KEY_RELEASE) {
383 state |= IBUS_RELEASE_MASK;
386 if (_use_sync_mode) {
387 retval = ibus_input_context_process_key_event (
390 event->hardware_keycode - 8,
394 ibus_input_context_process_key_event_async (
397 event->hardware_keycode - 8,
401 _process_key_event_done,
402 gdk_event_copy ((GdkEvent *) event));
408 event->state |= IBUS_HANDLED_MASK;
411 event->state |= IBUS_IGNORED_MASK;
418 _get_boolean_env(const gchar *name,
421 const gchar *value = g_getenv (name);
426 if (g_strcmp0 (value, "") == 0 ||
427 g_strcmp0 (value, "0") == 0 ||
428 g_strcmp0 (value, "false") == 0 ||
429 g_strcmp0 (value, "False") == 0 ||
430 g_strcmp0 (value, "FALSE") == 0)
437 ibus_im_context_class_init (IBusIMContextClass *class)
439 IDEBUG ("%s", __FUNCTION__);
441 GtkIMContextClass *im_context_class = GTK_IM_CONTEXT_CLASS (class);
442 GObjectClass *gobject_class = G_OBJECT_CLASS (class);
444 parent_class = (GtkIMContextClass *) g_type_class_peek_parent (class);
446 im_context_class->reset = ibus_im_context_reset;
447 im_context_class->focus_in = ibus_im_context_focus_in;
448 im_context_class->focus_out = ibus_im_context_focus_out;
449 im_context_class->filter_keypress = ibus_im_context_filter_keypress;
450 im_context_class->get_preedit_string = ibus_im_context_get_preedit_string;
451 im_context_class->set_client_window = ibus_im_context_set_client_window;
452 im_context_class->set_cursor_location = ibus_im_context_set_cursor_location;
453 im_context_class->set_use_preedit = ibus_im_context_set_use_preedit;
454 im_context_class->set_surrounding = ibus_im_context_set_surrounding;
455 gobject_class->finalize = ibus_im_context_finalize;
458 g_signal_lookup ("commit", G_TYPE_FROM_CLASS (class));
459 g_assert (_signal_commit_id != 0);
461 _signal_preedit_changed_id =
462 g_signal_lookup ("preedit-changed", G_TYPE_FROM_CLASS (class));
463 g_assert (_signal_preedit_changed_id != 0);
465 _signal_preedit_start_id =
466 g_signal_lookup ("preedit-start", G_TYPE_FROM_CLASS (class));
467 g_assert (_signal_preedit_start_id != 0);
469 _signal_preedit_end_id =
470 g_signal_lookup ("preedit-end", G_TYPE_FROM_CLASS (class));
471 g_assert (_signal_preedit_end_id != 0);
473 _signal_delete_surrounding_id =
474 g_signal_lookup ("delete-surrounding", G_TYPE_FROM_CLASS (class));
475 g_assert (_signal_delete_surrounding_id != 0);
477 _signal_retrieve_surrounding_id =
478 g_signal_lookup ("retrieve-surrounding", G_TYPE_FROM_CLASS (class));
479 g_assert (_signal_retrieve_surrounding_id != 0);
481 _use_key_snooper = !_get_boolean_env ("IBUS_DISABLE_SNOOPER",
483 _use_sync_mode = _get_boolean_env ("IBUS_ENABLE_SYNC_MODE", FALSE);
485 /* env IBUS_DISABLE_SNOOPER does not exist */
486 if (_use_key_snooper) {
487 /* disable snooper if app is in _no_snooper_apps */
488 const gchar * prgname = g_get_prgname ();
489 if (g_getenv ("IBUS_NO_SNOOPER_APPS")) {
490 _no_snooper_apps = g_getenv ("IBUS_NO_SNOOPER_APPS");
493 gchar ** apps = g_strsplit (_no_snooper_apps, ",", 0);
494 for (p = apps; *p != NULL; p++) {
495 if (g_regex_match_simple (*p, prgname, 0, 0)) {
496 _use_key_snooper = FALSE;
503 /* init bus object */
505 ibus_set_display (gdk_display_get_name (gdk_display_get_default ()));
506 _bus = ibus_bus_new ();
508 /* init the global fake context */
509 if (ibus_bus_is_connected (_bus)) {
510 _create_fake_input_context ();
513 g_signal_connect (_bus, "connected", G_CALLBACK (_bus_connected_cb), NULL);
517 /* always install snooper */
518 if (_key_snooper_id == 0)
519 _key_snooper_id = gtk_key_snooper_install (_key_snooper_cb, NULL);
523 ibus_im_context_class_fini (IBusIMContextClass *class)
525 if (_key_snooper_id != 0) {
526 IDEBUG ("snooper is terminated.");
527 gtk_key_snooper_remove (_key_snooper_id);
532 /* Copied from gtk+2.0-2.20.1/modules/input/imcedilla.c to fix crosbug.com/11421.
533 * Overwrite the original Gtk+'s compose table in gtk+-2.x.y/gtk/gtkimcontextsimple.c. */
535 /* The difference between this and the default input method is the handling
536 * of C+acute - this method produces C WITH CEDILLA rather than C WITH ACUTE.
537 * For languages that use CCedilla and not acute, this is the preferred mapping,
538 * and is particularly important for pt_BR, where the us-intl keyboard is
541 static guint16 cedilla_compose_seqs[] = {
542 #ifdef DEPRECATED_GDK_KEYSYMS
543 GDK_dead_acute, GDK_C, 0, 0, 0, 0x00C7, /* LATIN_CAPITAL_LETTER_C_WITH_CEDILLA */
544 GDK_dead_acute, GDK_c, 0, 0, 0, 0x00E7, /* LATIN_SMALL_LETTER_C_WITH_CEDILLA */
545 GDK_Multi_key, GDK_apostrophe, GDK_C, 0, 0, 0x00C7, /* LATIN_CAPITAL_LETTER_C_WITH_CEDILLA */
546 GDK_Multi_key, GDK_apostrophe, GDK_c, 0, 0, 0x00E7, /* LATIN_SMALL_LETTER_C_WITH_CEDILLA */
547 GDK_Multi_key, GDK_C, GDK_apostrophe, 0, 0, 0x00C7, /* LATIN_CAPITAL_LETTER_C_WITH_CEDILLA */
548 GDK_Multi_key, GDK_c, GDK_apostrophe, 0, 0, 0x00E7, /* LATIN_SMALL_LETTER_C_WITH_CEDILLA */
550 GDK_KEY_dead_acute, GDK_KEY_C, 0, 0, 0, 0x00C7, /* LATIN_CAPITAL_LETTER_C_WITH_CEDILLA */
551 GDK_KEY_dead_acute, GDK_KEY_c, 0, 0, 0, 0x00E7, /* LATIN_SMALL_LETTER_C_WITH_CEDILLA */
552 GDK_KEY_Multi_key, GDK_KEY_apostrophe, GDK_KEY_C, 0, 0, 0x00C7, /* LATIN_CAPITAL_LETTER_C_WITH_CEDILLA */
553 GDK_KEY_Multi_key, GDK_KEY_apostrophe, GDK_KEY_c, 0, 0, 0x00E7, /* LATIN_SMALL_LETTER_C_WITH_CEDILLA */
554 GDK_KEY_Multi_key, GDK_KEY_C, GDK_KEY_apostrophe, 0, 0, 0x00C7, /* LATIN_CAPITAL_LETTER_C_WITH_CEDILLA */
555 GDK_KEY_Multi_key, GDK_KEY_c, GDK_KEY_apostrophe, 0, 0, 0x00E7, /* LATIN_SMALL_LETTER_C_WITH_CEDILLA */
560 ibus_im_context_init (GObject *obj)
562 IDEBUG ("%s", __FUNCTION__);
564 IBusIMContext *ibusimcontext = IBUS_IM_CONTEXT (obj);
566 ibusimcontext->client_window = NULL;
569 ibusimcontext->enable = FALSE;
571 // Init preedit status
572 ibusimcontext->preedit_string = NULL;
573 ibusimcontext->preedit_attrs = NULL;
574 ibusimcontext->preedit_cursor_pos = 0;
575 ibusimcontext->preedit_visible = FALSE;
578 ibusimcontext->cursor_area.x = -1;
579 ibusimcontext->cursor_area.y = -1;
580 ibusimcontext->cursor_area.width = 0;
581 ibusimcontext->cursor_area.height = 0;
583 ibusimcontext->ibuscontext = NULL;
584 ibusimcontext->has_focus = FALSE;
585 ibusimcontext->time = GDK_CURRENT_TIME;
586 #ifdef ENABLE_SURROUNDING
587 ibusimcontext->caps = IBUS_CAP_PREEDIT_TEXT | IBUS_CAP_FOCUS | IBUS_CAP_SURROUNDING_TEXT;
589 ibusimcontext->caps = IBUS_CAP_PREEDIT_TEXT | IBUS_CAP_FOCUS;
593 // Create slave im context
594 ibusimcontext->slave = gtk_im_context_simple_new ();
595 gtk_im_context_simple_add_table (GTK_IM_CONTEXT_SIMPLE (ibusimcontext->slave),
596 cedilla_compose_seqs,
598 G_N_ELEMENTS (cedilla_compose_seqs) / (4 + 2));
600 g_signal_connect (ibusimcontext->slave,
602 G_CALLBACK (_slave_commit_cb),
604 g_signal_connect (ibusimcontext->slave,
606 G_CALLBACK (_slave_preedit_start_cb),
608 g_signal_connect (ibusimcontext->slave,
610 G_CALLBACK (_slave_preedit_end_cb),
612 g_signal_connect (ibusimcontext->slave,
614 G_CALLBACK (_slave_preedit_changed_cb),
616 g_signal_connect (ibusimcontext->slave,
617 "retrieve-surrounding",
618 G_CALLBACK (_slave_retrieve_surrounding_cb),
620 g_signal_connect (ibusimcontext->slave,
621 "delete-surrounding",
622 G_CALLBACK (_slave_delete_surrounding_cb),
625 if (ibus_bus_is_connected (_bus)) {
626 _create_input_context (ibusimcontext);
629 g_signal_connect (_bus, "connected", G_CALLBACK (_bus_connected_cb), obj);
633 ibus_im_context_finalize (GObject *obj)
635 IDEBUG ("%s", __FUNCTION__);
637 IBusIMContext *ibusimcontext = IBUS_IM_CONTEXT (obj);
639 g_signal_handlers_disconnect_by_func (_bus, G_CALLBACK (_bus_connected_cb), obj);
641 if (ibusimcontext->ibuscontext) {
642 ibus_proxy_destroy ((IBusProxy *)ibusimcontext->ibuscontext);
645 ibus_im_context_set_client_window ((GtkIMContext *)ibusimcontext, NULL);
647 if (ibusimcontext->slave) {
648 g_object_unref (ibusimcontext->slave);
649 ibusimcontext->slave = NULL;
653 if (ibusimcontext->preedit_string) {
654 g_free (ibusimcontext->preedit_string);
656 if (ibusimcontext->preedit_attrs) {
657 pango_attr_list_unref (ibusimcontext->preedit_attrs);
660 G_OBJECT_CLASS(parent_class)->finalize (obj);
664 ibus_im_context_filter_keypress (GtkIMContext *context,
667 IDEBUG ("%s", __FUNCTION__);
669 IBusIMContext *ibusimcontext = IBUS_IM_CONTEXT (context);
671 if (G_LIKELY (ibusimcontext->ibuscontext && ibusimcontext->has_focus)) {
672 /* If context does not have focus, ibus will process key event in sync mode.
673 * It is a workaround for increase search in treeview.
675 gboolean retval = FALSE;
677 if (event->state & IBUS_HANDLED_MASK)
680 if (event->state & IBUS_IGNORED_MASK)
681 return gtk_im_context_filter_keypress (ibusimcontext->slave, event);
683 /* XXX it is a workaround for some applications do not set client window. */
684 if (ibusimcontext->client_window == NULL && event->window != NULL)
685 gtk_im_context_set_client_window ((GtkIMContext *)ibusimcontext, event->window);
687 _request_surrounding_text (ibusimcontext, FALSE);
689 if (ibusimcontext != NULL) {
690 ibusimcontext->time = event->time;
693 guint state = event->state;
694 if (event->type == GDK_KEY_RELEASE) {
695 state |= IBUS_RELEASE_MASK;
698 if (_use_sync_mode) {
699 retval = ibus_input_context_process_key_event (
700 ibusimcontext->ibuscontext,
702 event->hardware_keycode - 8,
706 ibus_input_context_process_key_event_async (
707 ibusimcontext->ibuscontext,
709 event->hardware_keycode - 8,
713 _process_key_event_done,
714 gdk_event_copy ((GdkEvent *) event));
719 event->state |= IBUS_HANDLED_MASK;
723 event->state |= IBUS_IGNORED_MASK;
724 return gtk_im_context_filter_keypress (ibusimcontext->slave, event);
728 return gtk_im_context_filter_keypress (ibusimcontext->slave, event);
733 ibus_im_context_focus_in (GtkIMContext *context)
735 IDEBUG ("%s", __FUNCTION__);
737 IBusIMContext *ibusimcontext = (IBusIMContext *) context;
739 if (ibusimcontext->has_focus)
742 if (_focus_im_context != NULL) {
743 g_assert (_focus_im_context != context);
744 gtk_im_context_focus_out (_focus_im_context);
745 g_assert (_focus_im_context == NULL);
748 /* focus out fake context */
749 if (_fake_context != NULL) {
750 ibus_input_context_focus_out (_fake_context);
754 ibusimcontext->has_focus = TRUE;
755 if (ibusimcontext->ibuscontext) {
756 ibus_input_context_focus_in (ibusimcontext->ibuscontext);
759 gtk_im_context_focus_in (ibusimcontext->slave);
761 /* set_cursor_location_internal() will get origin from X server,
762 * it blocks UI. So delay it to idle callback. */
763 g_idle_add_full (G_PRIORITY_DEFAULT_IDLE,
764 (GSourceFunc) _set_cursor_location_internal,
765 g_object_ref (ibusimcontext),
766 (GDestroyNotify) g_object_unref);
768 /* retrieve the initial surrounding-text (regardless of whether
769 * the current IBus engine needs surrounding-text) */
770 _request_surrounding_text (ibusimcontext, TRUE);
772 g_object_add_weak_pointer ((GObject *) context,
773 (gpointer *) &_focus_im_context);
774 _focus_im_context = context;
778 ibus_im_context_focus_out (GtkIMContext *context)
780 IDEBUG ("%s", __FUNCTION__);
781 IBusIMContext *ibusimcontext = (IBusIMContext *) context;
783 if (ibusimcontext->has_focus == FALSE) {
787 g_assert (context == _focus_im_context);
788 g_object_remove_weak_pointer ((GObject *) context,
789 (gpointer *) &_focus_im_context);
790 _focus_im_context = NULL;
792 ibusimcontext->has_focus = FALSE;
793 if (ibusimcontext->ibuscontext) {
794 ibus_input_context_focus_out (ibusimcontext->ibuscontext);
797 gtk_im_context_focus_out (ibusimcontext->slave);
799 /* focus in the fake ic */
800 if (_fake_context != NULL) {
801 ibus_input_context_focus_in (_fake_context);
806 ibus_im_context_reset (GtkIMContext *context)
808 IDEBUG ("%s", __FUNCTION__);
810 IBusIMContext *ibusimcontext = IBUS_IM_CONTEXT (context);
812 if (ibusimcontext->ibuscontext) {
813 ibus_input_context_reset (ibusimcontext->ibuscontext);
815 gtk_im_context_reset (ibusimcontext->slave);
820 ibus_im_context_get_preedit_string (GtkIMContext *context,
822 PangoAttrList **attrs,
825 IDEBUG ("%s", __FUNCTION__);
827 IBusIMContext *ibusimcontext = IBUS_IM_CONTEXT (context);
829 if (ibusimcontext->enable) {
830 if (ibusimcontext->preedit_visible) {
832 *str = g_strdup (ibusimcontext->preedit_string ? ibusimcontext->preedit_string: "");
836 *attrs = ibusimcontext->preedit_attrs ?
837 pango_attr_list_ref (ibusimcontext->preedit_attrs):
838 pango_attr_list_new ();
842 *cursor_pos = ibusimcontext->preedit_cursor_pos;
847 *str = g_strdup ("");
850 *attrs = pango_attr_list_new ();
858 gtk_im_context_get_preedit_string (ibusimcontext->slave, str, attrs, cursor_pos);
860 IDEBUG ("str=%s", *str);
865 ibus_im_context_set_client_window (GtkIMContext *context, GdkWindow *client)
867 IDEBUG ("%s", __FUNCTION__);
869 IBusIMContext *ibusimcontext = IBUS_IM_CONTEXT (context);
871 if (ibusimcontext->client_window) {
872 g_object_unref (ibusimcontext->client_window);
873 ibusimcontext->client_window = NULL;
877 ibusimcontext->client_window = g_object_ref (client);
879 if (ibusimcontext->slave)
880 gtk_im_context_set_client_window (ibusimcontext->slave, client);
884 _set_cursor_location_internal (IBusIMContext *ibusimcontext)
888 if(ibusimcontext->client_window == NULL ||
889 ibusimcontext->ibuscontext == NULL) {
893 area = ibusimcontext->cursor_area;
894 if (area.x == -1 && area.y == -1 && area.width == 0 && area.height == 0) {
895 #if GTK_CHECK_VERSION (2, 91, 0)
897 area.y += gdk_window_get_height (ibusimcontext->client_window);
900 gdk_drawable_get_size (ibusimcontext->client_window, &w, &h);
906 gdk_window_get_root_coords (ibusimcontext->client_window,
909 ibus_input_context_set_cursor_location (ibusimcontext->ibuscontext,
918 ibus_im_context_set_cursor_location (GtkIMContext *context, GdkRectangle *area)
920 IDEBUG ("%s", __FUNCTION__);
922 IBusIMContext *ibusimcontext = IBUS_IM_CONTEXT (context);
924 if (ibusimcontext->cursor_area.x == area->x &&
925 ibusimcontext->cursor_area.y == area->y &&
926 ibusimcontext->cursor_area.width == area->width &&
927 ibusimcontext->cursor_area.height == area->height) {
930 ibusimcontext->cursor_area = *area;
931 _set_cursor_location_internal (ibusimcontext);
932 gtk_im_context_set_cursor_location (ibusimcontext->slave, area);
936 ibus_im_context_set_use_preedit (GtkIMContext *context, gboolean use_preedit)
938 IDEBUG ("%s", __FUNCTION__);
940 IBusIMContext *ibusimcontext = IBUS_IM_CONTEXT (context);
942 if(ibusimcontext->ibuscontext) {
944 ibusimcontext->caps |= IBUS_CAP_PREEDIT_TEXT;
947 ibusimcontext->caps &= ~IBUS_CAP_PREEDIT_TEXT;
949 ibus_input_context_set_capabilities (ibusimcontext->ibuscontext,
950 ibusimcontext->caps);
952 gtk_im_context_set_use_preedit (ibusimcontext->slave, use_preedit);
956 ibus_im_context_set_surrounding (GtkIMContext *context,
961 g_return_if_fail (context != NULL);
962 g_return_if_fail (IBUS_IS_IM_CONTEXT (context));
963 g_return_if_fail (text != NULL);
964 g_return_if_fail (strlen (text) >= len);
965 g_return_if_fail (0 <= cursor_index && cursor_index <= len);
967 IBusIMContext *ibusimcontext = IBUS_IM_CONTEXT (context);
969 if (ibusimcontext->enable && ibusimcontext->ibuscontext) {
974 p = g_strndup (text, len);
975 cursor_pos = g_utf8_strlen (p, cursor_index);
976 ibustext = ibus_text_new_from_string (p);
978 ibus_input_context_set_surrounding_text (ibusimcontext->ibuscontext,
982 gtk_im_context_set_surrounding (ibusimcontext->slave,
989 _bus_connected_cb (IBusBus *bus,
990 IBusIMContext *ibusimcontext)
992 IDEBUG ("%s", __FUNCTION__);
994 _create_input_context (ibusimcontext);
996 _create_fake_input_context ();
1000 _ibus_context_commit_text_cb (IBusInputContext *ibuscontext,
1002 IBusIMContext *ibusimcontext)
1004 IDEBUG ("%s", __FUNCTION__);
1006 g_signal_emit (ibusimcontext, _signal_commit_id, 0, text->text);
1008 _request_surrounding_text (ibusimcontext, FALSE);
1012 _key_is_modifier (guint keyval)
1014 /* See gdkkeys-x11.c:_gdk_keymap_key_is_modifier() for how this
1015 * really should be implemented */
1018 #ifdef DEPRECATED_GDK_KEYSYMS
1024 case GDK_Shift_Lock:
1034 case GDK_ISO_Level2_Latch:
1035 case GDK_ISO_Level3_Shift:
1036 case GDK_ISO_Level3_Latch:
1037 case GDK_ISO_Level3_Lock:
1038 case GDK_ISO_Level5_Shift:
1039 case GDK_ISO_Level5_Latch:
1040 case GDK_ISO_Level5_Lock:
1041 case GDK_ISO_Group_Shift:
1042 case GDK_ISO_Group_Latch:
1043 case GDK_ISO_Group_Lock:
1046 case GDK_KEY_Shift_L:
1047 case GDK_KEY_Shift_R:
1048 case GDK_KEY_Control_L:
1049 case GDK_KEY_Control_R:
1050 case GDK_KEY_Caps_Lock:
1051 case GDK_KEY_Shift_Lock:
1052 case GDK_KEY_Meta_L:
1053 case GDK_KEY_Meta_R:
1056 case GDK_KEY_Super_L:
1057 case GDK_KEY_Super_R:
1058 case GDK_KEY_Hyper_L:
1059 case GDK_KEY_Hyper_R:
1060 case GDK_KEY_ISO_Lock:
1061 case GDK_KEY_ISO_Level2_Latch:
1062 case GDK_KEY_ISO_Level3_Shift:
1063 case GDK_KEY_ISO_Level3_Latch:
1064 case GDK_KEY_ISO_Level3_Lock:
1065 case GDK_KEY_ISO_Level5_Shift:
1066 case GDK_KEY_ISO_Level5_Latch:
1067 case GDK_KEY_ISO_Level5_Lock:
1068 case GDK_KEY_ISO_Group_Shift:
1069 case GDK_KEY_ISO_Group_Latch:
1070 case GDK_KEY_ISO_Group_Lock:
1078 static GdkEventKey *
1079 _create_gdk_event (IBusIMContext *ibusimcontext,
1087 GdkEventKey *event = (GdkEventKey *)gdk_event_new ((state & IBUS_RELEASE_MASK) ? GDK_KEY_RELEASE : GDK_KEY_PRESS);
1089 if (ibusimcontext && ibusimcontext->client_window)
1090 event->window = g_object_ref (ibusimcontext->client_window);
1091 else if (_input_window)
1092 event->window = g_object_ref (_input_window);
1094 /* The time is copied the latest value from the previous
1095 * GdkKeyEvent in filter_keypress().
1097 * We understand the best way would be to pass the all time value
1098 * to IBus functions process_key_event() and IBus DBus functions
1099 * ProcessKeyEvent() in IM clients and IM engines so that the
1100 * _create_gdk_event() could get the correct time values.
1101 * However it would causes to change many functions and the time value
1102 * would not provide the useful meanings for each IBus engines but just
1103 * pass the original value to ForwardKeyEvent().
1104 * We use the saved value at the moment.
1106 * Another idea might be to have the time implementation in X servers
1107 * but some Xorg uses clock_gettime() and others use gettimeofday()
1108 * and the values would be different in each implementation and
1109 * locale/remote X server. So probably that idea would not work. */
1110 if (ibusimcontext) {
1111 event->time = ibusimcontext->time;
1113 event->time = GDK_CURRENT_TIME;
1116 event->send_event = FALSE;
1117 event->state = state;
1118 event->keyval = keyval;
1119 event->string = NULL;
1121 event->hardware_keycode = (keycode != 0) ? keycode + 8 : 0;
1123 event->is_modifier = _key_is_modifier (keyval);
1125 #ifdef DEPRECATED_GDK_KEYSYMS
1126 if (keyval != GDK_VoidSymbol)
1128 if (keyval != GDK_KEY_VoidSymbol)
1130 c = gdk_keyval_to_unicode (keyval);
1133 gsize bytes_written;
1136 /* Apply the control key - Taken from Xlib
1138 if (event->state & GDK_CONTROL_MASK) {
1139 if ((c >= '@' && c < '\177') || c == ' ') c &= 0x1F;
1140 else if (c == '2') {
1141 event->string = g_memdup ("\0\0", 2);
1146 else if (c >= '3' && c <= '7') c -= ('3' - '\033');
1147 else if (c == '8') c = '\177';
1148 else if (c == '/') c = '_' & 0x1F;
1151 len = g_unichar_to_utf8 (c, buf);
1154 event->string = g_locale_from_utf8 (buf, len,
1155 NULL, &bytes_written,
1158 event->length = bytes_written;
1159 #ifdef DEPRECATED_GDK_KEYSYMS
1160 } else if (keyval == GDK_Escape) {
1162 } else if (keyval == GDK_KEY_Escape) {
1165 event->string = g_strdup ("\033");
1167 #ifdef DEPRECATED_GDK_KEYSYMS
1168 else if (keyval == GDK_Return ||
1169 keyval == GDK_KP_Enter) {
1171 else if (keyval == GDK_KEY_Return ||
1172 keyval == GDK_KEY_KP_Enter) {
1175 event->string = g_strdup ("\r");
1178 if (!event->string) {
1180 event->string = g_strdup ("");
1187 _ibus_context_forward_key_event_cb (IBusInputContext *ibuscontext,
1191 IBusIMContext *ibusimcontext)
1193 IDEBUG ("%s", __FUNCTION__);
1194 GdkEventKey *event = _create_gdk_event (ibusimcontext, keyval, keycode, state);
1195 gdk_event_put ((GdkEvent *)event);
1196 gdk_event_free ((GdkEvent *)event);
1200 _ibus_context_delete_surrounding_text_cb (IBusInputContext *ibuscontext,
1201 gint offset_from_cursor,
1203 IBusIMContext *ibusimcontext)
1205 gboolean return_value;
1206 g_signal_emit (ibusimcontext, _signal_delete_surrounding_id, 0, offset_from_cursor, nchars, &return_value);
1210 _ibus_context_update_preedit_text_cb (IBusInputContext *ibuscontext,
1214 IBusIMContext *ibusimcontext)
1216 IDEBUG ("%s", __FUNCTION__);
1221 if (ibusimcontext->preedit_string) {
1222 g_free (ibusimcontext->preedit_string);
1224 if (ibusimcontext->preedit_attrs) {
1225 pango_attr_list_unref (ibusimcontext->preedit_attrs);
1226 ibusimcontext->preedit_attrs = NULL;
1230 ibusimcontext->preedit_string = g_strdup (str);
1233 ibusimcontext->preedit_attrs = pango_attr_list_new ();
1234 for (i = 0; ; i++) {
1235 IBusAttribute *attr = ibus_attr_list_get (text->attrs, i);
1240 PangoAttribute *pango_attr;
1241 switch (attr->type) {
1242 case IBUS_ATTR_TYPE_UNDERLINE:
1243 pango_attr = pango_attr_underline_new (attr->value);
1245 case IBUS_ATTR_TYPE_FOREGROUND:
1246 pango_attr = pango_attr_foreground_new (
1247 ((attr->value & 0xff0000) >> 8) | 0xff,
1248 ((attr->value & 0x00ff00)) | 0xff,
1249 ((attr->value & 0x0000ff) << 8) | 0xff);
1251 case IBUS_ATTR_TYPE_BACKGROUND:
1252 pango_attr = pango_attr_background_new (
1253 ((attr->value & 0xff0000) >> 8) | 0xff,
1254 ((attr->value & 0x00ff00)) | 0xff,
1255 ((attr->value & 0x0000ff) << 8) | 0xff);
1260 pango_attr->start_index = g_utf8_offset_to_pointer (str, attr->start_index) - str;
1261 pango_attr->end_index = g_utf8_offset_to_pointer (str, attr->end_index) - str;
1262 pango_attr_list_insert (ibusimcontext->preedit_attrs, pango_attr);
1266 ibusimcontext->preedit_cursor_pos = cursor_pos;
1268 flag = ibusimcontext->preedit_visible != visible;
1269 ibusimcontext->preedit_visible = visible;
1271 if (ibusimcontext->preedit_visible) {
1273 /* invisible => visible */
1274 g_signal_emit (ibusimcontext, _signal_preedit_start_id, 0);
1276 g_signal_emit (ibusimcontext, _signal_preedit_changed_id, 0);
1280 /* visible => invisible */
1281 g_signal_emit (ibusimcontext, _signal_preedit_changed_id, 0);
1282 g_signal_emit (ibusimcontext, _signal_preedit_end_id, 0);
1285 /* still invisible */
1292 _ibus_context_show_preedit_text_cb (IBusInputContext *ibuscontext,
1293 IBusIMContext *ibusimcontext)
1295 IDEBUG ("%s", __FUNCTION__);
1297 if (ibusimcontext->preedit_visible == TRUE)
1300 ibusimcontext->preedit_visible = TRUE;
1301 g_signal_emit (ibusimcontext, _signal_preedit_start_id, 0);
1302 g_signal_emit (ibusimcontext, _signal_preedit_changed_id, 0);
1304 _request_surrounding_text (ibusimcontext, FALSE);
1308 _ibus_context_hide_preedit_text_cb (IBusInputContext *ibuscontext,
1309 IBusIMContext *ibusimcontext)
1311 IDEBUG ("%s", __FUNCTION__);
1313 if (ibusimcontext->preedit_visible == FALSE)
1316 ibusimcontext->preedit_visible = FALSE;
1317 g_signal_emit (ibusimcontext, _signal_preedit_changed_id, 0);
1318 g_signal_emit (ibusimcontext, _signal_preedit_end_id, 0);
1322 _ibus_context_enabled_cb (IBusInputContext *ibuscontext,
1323 IBusIMContext *ibusimcontext)
1325 IDEBUG ("%s", __FUNCTION__);
1327 ibusimcontext->enable = TRUE;
1329 /* retrieve the initial surrounding-text (regardless of whether
1330 * the current IBus engine needs surrounding-text) */
1331 _request_surrounding_text (ibusimcontext, TRUE);
1335 _ibus_context_disabled_cb (IBusInputContext *ibuscontext,
1336 IBusIMContext *ibusimcontext)
1338 IDEBUG ("%s", __FUNCTION__);
1339 ibusimcontext->enable = FALSE;
1342 ibusimcontext->preedit_visible = FALSE;
1343 ibusimcontext->preedit_cursor_pos = 0;
1344 g_free (ibusimcontext->preedit_string);
1345 ibusimcontext->preedit_string = NULL;
1347 g_signal_emit (ibusimcontext, _signal_preedit_changed_id, 0);
1348 g_signal_emit (ibusimcontext, _signal_preedit_end_id, 0);
1352 _ibus_context_destroy_cb (IBusInputContext *ibuscontext,
1353 IBusIMContext *ibusimcontext)
1355 IDEBUG ("%s", __FUNCTION__);
1356 g_assert (ibusimcontext->ibuscontext == ibuscontext);
1358 g_object_unref (ibusimcontext->ibuscontext);
1359 ibusimcontext->ibuscontext = NULL;
1361 ibusimcontext->enable = FALSE;
1364 ibusimcontext->preedit_visible = FALSE;
1365 ibusimcontext->preedit_cursor_pos = 0;
1366 g_free (ibusimcontext->preedit_string);
1367 ibusimcontext->preedit_string = NULL;
1369 g_signal_emit (ibusimcontext, _signal_preedit_changed_id, 0);
1370 g_signal_emit (ibusimcontext, _signal_preedit_end_id, 0);
1374 _create_input_context_done (IBusBus *bus,
1376 IBusIMContext *ibusimcontext)
1378 GError *error = NULL;
1379 IBusInputContext *context = ibus_bus_create_input_context_async_finish (
1382 if (ibusimcontext->cancellable != NULL) {
1383 g_object_unref (ibusimcontext->cancellable);
1384 ibusimcontext->cancellable = NULL;
1387 if (context == NULL) {
1388 g_warning ("Create input context failed: %s.", error->message);
1389 g_error_free (error);
1393 ibusimcontext->ibuscontext = context;
1395 g_signal_connect (ibusimcontext->ibuscontext,
1397 G_CALLBACK (_ibus_context_commit_text_cb),
1399 g_signal_connect (ibusimcontext->ibuscontext,
1400 "forward-key-event",
1401 G_CALLBACK (_ibus_context_forward_key_event_cb),
1403 g_signal_connect (ibusimcontext->ibuscontext,
1404 "delete-surrounding-text",
1405 G_CALLBACK (_ibus_context_delete_surrounding_text_cb),
1407 g_signal_connect (ibusimcontext->ibuscontext,
1408 "update-preedit-text",
1409 G_CALLBACK (_ibus_context_update_preedit_text_cb),
1411 g_signal_connect (ibusimcontext->ibuscontext,
1412 "show-preedit-text",
1413 G_CALLBACK (_ibus_context_show_preedit_text_cb),
1415 g_signal_connect (ibusimcontext->ibuscontext,
1416 "hide-preedit-text",
1417 G_CALLBACK (_ibus_context_hide_preedit_text_cb),
1419 g_signal_connect (ibusimcontext->ibuscontext,
1421 G_CALLBACK (_ibus_context_enabled_cb),
1423 g_signal_connect (ibusimcontext->ibuscontext,
1425 G_CALLBACK (_ibus_context_disabled_cb),
1427 g_signal_connect (ibusimcontext->ibuscontext, "destroy",
1428 G_CALLBACK (_ibus_context_destroy_cb),
1431 ibus_input_context_set_capabilities (ibusimcontext->ibuscontext, ibusimcontext->caps);
1433 if (ibusimcontext->has_focus) {
1434 ibus_input_context_focus_in (ibusimcontext->ibuscontext);
1435 _set_cursor_location_internal (ibusimcontext);
1439 g_object_unref (ibusimcontext);
1443 _create_input_context (IBusIMContext *ibusimcontext)
1445 IDEBUG ("%s", __FUNCTION__);
1447 g_assert (ibusimcontext->ibuscontext == NULL);
1449 if (ibusimcontext->cancellable != NULL) {
1450 /* Cancel previous create input context request */
1451 g_cancellable_cancel (ibusimcontext->cancellable);
1452 g_object_unref (ibusimcontext->cancellable);
1453 ibusimcontext->cancellable = NULL;
1456 ibusimcontext->cancellable = g_cancellable_new ();
1458 ibus_bus_create_input_context_async (_bus,
1460 ibusimcontext->cancellable,
1461 (GAsyncReadyCallback)_create_input_context_done,
1462 g_object_ref (ibusimcontext));
1465 /* Callback functions for slave context */
1467 _slave_commit_cb (GtkIMContext *slave,
1469 IBusIMContext *ibusimcontext)
1472 if ((GtkIMContext *)context == CURRENT_CONTEXT && ibus_im_client_is_enabled (_client))
1475 g_signal_emit (ibusimcontext, _signal_commit_id, 0, string);
1479 _slave_preedit_changed_cb (GtkIMContext *slave,
1480 IBusIMContext *ibusimcontext)
1482 if (ibusimcontext->enable && ibusimcontext->ibuscontext) {
1486 g_signal_emit (ibusimcontext, _signal_preedit_changed_id, 0);
1490 _slave_preedit_start_cb (GtkIMContext *slave,
1491 IBusIMContext *ibusimcontext)
1493 if (ibusimcontext->enable && ibusimcontext->ibuscontext) {
1497 g_signal_emit (ibusimcontext, _signal_preedit_start_id, 0);
1501 _slave_preedit_end_cb (GtkIMContext *slave,
1502 IBusIMContext *ibusimcontext)
1504 if (ibusimcontext->enable && ibusimcontext->ibuscontext) {
1507 g_signal_emit (ibusimcontext, _signal_preedit_end_id, 0);
1511 _slave_retrieve_surrounding_cb (GtkIMContext *slave,
1512 IBusIMContext *ibusimcontext)
1514 gboolean return_value;
1516 if (ibusimcontext->enable && ibusimcontext->ibuscontext) {
1519 g_signal_emit (ibusimcontext, _signal_retrieve_surrounding_id, 0,
1521 return return_value;
1525 _slave_delete_surrounding_cb (GtkIMContext *slave,
1526 gint offset_from_cursor,
1528 IBusIMContext *ibusimcontext)
1530 gboolean return_value;
1532 if (ibusimcontext->enable && ibusimcontext->ibuscontext) {
1535 g_signal_emit (ibusimcontext, _signal_delete_surrounding_id, 0, offset_from_cursor, nchars, &return_value);
1536 return return_value;
1541 _ibus_fake_context_destroy_cb (IBusInputContext *ibuscontext,
1544 /* The fack IC may be destroyed when the connection is lost.
1545 * Should release it. */
1546 g_assert (ibuscontext == _fake_context);
1547 g_object_unref (_fake_context);
1548 _fake_context = NULL;
1551 static GCancellable *_fake_cancellable = NULL;
1554 _create_fake_input_context_done (IBusBus *bus,
1556 IBusIMContext *ibusimcontext)
1558 GError *error = NULL;
1559 IBusInputContext *context = ibus_bus_create_input_context_async_finish (
1562 if (_fake_cancellable != NULL) {
1563 g_object_unref (_fake_cancellable);
1564 _fake_cancellable = NULL;
1567 if (context == NULL) {
1568 g_warning ("Create fake input context failed: %s.", error->message);
1569 g_error_free (error);
1573 _fake_context = context;
1575 g_signal_connect (_fake_context, "forward-key-event",
1576 G_CALLBACK (_ibus_context_forward_key_event_cb),
1578 g_signal_connect (_fake_context, "destroy",
1579 G_CALLBACK (_ibus_fake_context_destroy_cb),
1582 guint32 caps = IBUS_CAP_PREEDIT_TEXT | IBUS_CAP_FOCUS | IBUS_CAP_SURROUNDING_TEXT;
1583 ibus_input_context_set_capabilities (_fake_context, caps);
1585 /* focus in/out the fake context */
1586 if (_focus_im_context == NULL)
1587 ibus_input_context_focus_in (_fake_context);
1589 ibus_input_context_focus_out (_fake_context);
1593 _create_fake_input_context (void)
1595 g_return_if_fail (_fake_context == NULL);
1597 /* Global engine is always enabled in Chrome OS,
1598 * so create fake IC, and set focus if no other IC has focus.
1601 if (_fake_cancellable != NULL) {
1602 g_cancellable_cancel (_fake_cancellable);
1603 g_object_unref (_fake_cancellable);
1604 _fake_cancellable = NULL;
1607 _fake_cancellable = g_cancellable_new ();
1609 ibus_bus_create_input_context_async (_bus,
1612 (GAsyncReadyCallback)_create_fake_input_context_done,
1618 _create_fake_input_context (void)
1620 /* For Linux desktop, do not use fake IC. */