6 #include <sys/socket.h>
15 #include <Ecore_Evas.h>
18 #include "ibus_imcontext.h"
22 /* instance members */
23 Ecore_IMF_Context *ctx;
27 IBusInputContext *ibuscontext;
31 Eina_List *preedit_attrs;
32 int preedit_cursor_pos;
33 Eina_Bool preedit_visible;
42 Ecore_X_Window client_window;
48 typedef struct _KeyEvent KeyEvent;
56 static Eina_Bool _use_sync_mode = EINA_FALSE;
58 static Ecore_IMF_Context *_focus_im_context = NULL;
59 static IBusBus *_bus = NULL;
61 /* functions prototype */
63 static void _create_input_context (IBusIMContext *context);
64 static void _set_cursor_location_internal
65 (Ecore_IMF_Context *ctx);
66 static void _bus_connected_cb (IBusBus *bus,
67 IBusIMContext *context);
68 static XKeyEvent createXKeyEvent (Window win, Eina_Bool press, int keysym, int modifiers);
71 _window_to_screen_geometry_get(Ecore_X_Window client_win, int *x, int *y)
73 Ecore_X_Window root_window, win;
75 int sum_x = 0, sum_y = 0;
77 root_window = ecore_x_window_root_get(client_win);
80 while (root_window != win)
82 ecore_x_window_geometry_get(win, &win_x, &win_y, NULL, NULL);
85 win = ecore_x_window_parent_get(win);
95 _ecore_imf_modifier_to_ibus_modifier(unsigned int modifier)
97 unsigned int state = 0;
99 /**< "Control" is pressed */
100 if (modifier & ECORE_IMF_KEYBOARD_MODIFIER_CTRL)
101 state |= IBUS_CONTROL_MASK;
103 /**< "Alt" is pressed */
104 if (modifier & ECORE_IMF_KEYBOARD_MODIFIER_ALT)
105 state |= IBUS_MOD1_MASK;
107 /**< "Shift" is pressed */
108 if (modifier & ECORE_IMF_KEYBOARD_MODIFIER_SHIFT)
109 state |= IBUS_SHIFT_MASK;
111 /**< "Win" (between "Ctrl" and "Alt") */
112 if (modifier & ECORE_IMF_KEYBOARD_MODIFIER_WIN)
113 state |= IBUS_SUPER_MASK;
115 /**< "AltGr" is pressed */
116 if (modifier & ECORE_IMF_KEYBOARD_MODIFIER_ALTGR)
117 state |= IBUS_MOD5_MASK;
123 key_event_put(int keysym, int state)
125 // Find the window which has the current keyboard focus.
127 int revert = RevertToParent;
129 XGetInputFocus(ecore_x_display_get(), &winFocus, &revert);
132 if (state & IBUS_RELEASE_MASK)
134 event = createXKeyEvent(winFocus, EINA_FALSE, keysym, state);
135 XSendEvent(event.display, event.window, True, KeyReleaseMask, (XEvent *)&event);
139 event = createXKeyEvent(winFocus, EINA_TRUE, keysym, state);
140 XSendEvent(event.display, event.window, True, KeyPressMask, (XEvent *)&event);
145 key_event_copy(int keysym, int state)
147 KeyEvent *kev = calloc(1, sizeof(KeyEvent));
148 kev->keysym = keysym;
155 ibus_im_context_new(void)
157 EINA_LOG_DBG("%s", __FUNCTION__);
159 IBusIMContext *context = calloc(1, sizeof(IBusIMContext));
161 /* init bus object */
164 char *display_name = NULL;
166 if ((display_name = getenv("DISPLAY")))
167 ibus_set_display(display_name);
169 ibus_set_display(":0.0");
171 _bus = ibus_bus_new();
178 _process_key_event_done (GObject *object,
182 IBusInputContext *context = (IBusInputContext *)object;
183 KeyEvent *event = (KeyEvent *)user_data;
185 GError *error = NULL;
186 Eina_Bool retval = ibus_input_context_process_key_event_async_finish(context,
192 g_warning("Process Key Event failed: %s.", error->message);
196 if (retval == EINA_FALSE)
198 key_event_put(event->keysym, event->state);
204 ibus_im_context_add(Ecore_IMF_Context *ctx)
206 EINA_LOG_DBG("%s", __FUNCTION__);
209 IBusIMContext *ibusimcontext = (IBusIMContext *)ecore_imf_context_data_get(ctx);
210 EINA_SAFETY_ON_NULL_RETURN(ibusimcontext);
212 ibusimcontext->client_window = 0;
215 ibusimcontext->enable = EINA_FALSE;
217 // Init preedit status
218 ibusimcontext->preedit_string = NULL;
219 ibusimcontext->preedit_attrs = NULL;
220 ibusimcontext->preedit_cursor_pos = 0;
221 ibusimcontext->preedit_visible = EINA_FALSE;
224 ibusimcontext->cursor_x = -1;
225 ibusimcontext->cursor_y = -1;
226 ibusimcontext->cursor_w = 0;
227 ibusimcontext->cursor_h = 0;
229 ibusimcontext->ibuscontext = NULL;
230 ibusimcontext->has_focus = EINA_FALSE;
231 ibusimcontext->caps = IBUS_CAP_PREEDIT_TEXT | IBUS_CAP_FOCUS | IBUS_CAP_SURROUNDING_TEXT;
232 ibusimcontext->ctx = ctx;
234 s = getenv("IBUS_ENABLE_SYNC_MODE");
236 _use_sync_mode = !!atoi(s);
238 if (ibus_bus_is_connected(_bus))
239 _create_input_context (ibusimcontext);
241 g_signal_connect(_bus, "connected", G_CALLBACK (_bus_connected_cb), ctx);
245 ibus_im_context_del(Ecore_IMF_Context *ctx)
247 EINA_LOG_DBG("%s", __FUNCTION__);
249 IBusIMContext *ibusimcontext = (IBusIMContext*)ecore_imf_context_data_get(ctx);
250 EINA_SAFETY_ON_NULL_RETURN(ibusimcontext);
252 g_signal_handlers_disconnect_by_func(_bus, G_CALLBACK(_bus_connected_cb), ctx);
254 if (ibusimcontext->ibuscontext)
255 ibus_proxy_destroy((IBusProxy *)ibusimcontext->ibuscontext);
258 if (ibusimcontext->preedit_string)
259 free(ibusimcontext->preedit_string);
263 ibus_im_context_filter_event(Ecore_IMF_Context *ctx, Ecore_IMF_Event_Type type, Ecore_IMF_Event *event)
265 IBusIMContext *ibusimcontext = (IBusIMContext*)ecore_imf_context_data_get(ctx);
266 EINA_SAFETY_ON_NULL_RETURN_VAL(ibusimcontext, EINA_FALSE);
268 if (type != ECORE_IMF_EVENT_KEY_UP && type != ECORE_IMF_EVENT_KEY_DOWN)
271 EINA_LOG_DBG("%s", __FUNCTION__);
273 if (G_LIKELY(ibusimcontext->ibuscontext && ibusimcontext->has_focus))
275 /* If context does not have focus, ibus will process key event in sync mode.
276 * It is a workaround for increase search in treeview.
278 Eina_Bool retval = EINA_FALSE;
281 unsigned int state = 0;
283 if (type == ECORE_IMF_EVENT_KEY_UP)
285 Ecore_IMF_Event_Key_Up *ev = (Ecore_IMF_Event_Key_Up *)event;
286 if (ev->timestamp == 0)
289 keycode = ecore_x_keysym_keycode_get(ev->key);
290 keysym = XStringToKeysym(ev->key);
291 state = _ecore_imf_modifier_to_ibus_modifier(ev->modifiers) | IBUS_RELEASE_MASK;
295 retval = ibus_input_context_process_key_event(ibusimcontext->ibuscontext,
302 ibus_input_context_process_key_event_async(ibusimcontext->ibuscontext,
308 _process_key_event_done,
309 key_event_copy(keysym, state));
313 else if (type == ECORE_IMF_EVENT_KEY_DOWN)
315 Ecore_IMF_Event_Key_Down *ev = (Ecore_IMF_Event_Key_Down *)event;
316 if (ev->timestamp == 0)
319 keycode = ecore_x_keysym_keycode_get(ev->key);
320 keysym = XStringToKeysym(ev->key);
321 state = _ecore_imf_modifier_to_ibus_modifier(ev->modifiers);
324 retval = ibus_input_context_process_key_event(ibusimcontext->ibuscontext,
331 ibus_input_context_process_key_event_async(ibusimcontext->ibuscontext,
337 _process_key_event_done,
338 key_event_copy(keysym, state));
355 ibus_im_context_focus_in(Ecore_IMF_Context *ctx)
357 EINA_LOG_DBG("ctx : %p", ctx);
359 IBusIMContext *ibusimcontext = (IBusIMContext*)ecore_imf_context_data_get(ctx);
360 EINA_SAFETY_ON_NULL_RETURN(ibusimcontext);
362 if (ibusimcontext->has_focus)
365 if (_focus_im_context != NULL)
366 ecore_imf_context_focus_out(_focus_im_context);
368 ibusimcontext->has_focus = EINA_TRUE;
369 if (ibusimcontext->ibuscontext)
370 ibus_input_context_focus_in(ibusimcontext->ibuscontext);
372 if (_focus_im_context != ctx)
373 _focus_im_context = ctx;
377 ibus_im_context_focus_out(Ecore_IMF_Context *ctx)
379 EINA_LOG_DBG("ctx : %p", ctx);
381 IBusIMContext *ibusimcontext = (IBusIMContext *)ecore_imf_context_data_get(ctx);
382 EINA_SAFETY_ON_NULL_RETURN(ibusimcontext);
384 if (ibusimcontext->has_focus == EINA_FALSE)
387 if (_focus_im_context == ctx)
388 _focus_im_context = NULL;
390 ibusimcontext->has_focus = EINA_FALSE;
391 if (ibusimcontext->ibuscontext)
392 ibus_input_context_focus_out(ibusimcontext->ibuscontext);
396 ibus_im_context_reset(Ecore_IMF_Context *ctx)
398 IBusIMContext *ibusimcontext = (IBusIMContext*)ecore_imf_context_data_get(ctx);
399 EINA_SAFETY_ON_NULL_RETURN(ibusimcontext);
401 if (ibusimcontext->ibuscontext)
402 ibus_input_context_reset(ibusimcontext->ibuscontext);
406 ibus_im_context_preedit_string_get(Ecore_IMF_Context *ctx,
410 IBusIMContext *ibusimcontext = (IBusIMContext*)ecore_imf_context_data_get(ctx);
411 EINA_SAFETY_ON_NULL_RETURN(ibusimcontext);
413 if (ibusimcontext->enable && ibusimcontext->preedit_visible)
416 *str = strdup(ibusimcontext->preedit_string ? ibusimcontext->preedit_string: "");
419 *cursor_pos = ibusimcontext->preedit_cursor_pos;
429 EINA_LOG_DBG("str : %s, cursor_pos : %d", *str, *cursor_pos);
433 ibus_im_context_preedit_string_with_attributes_get(Ecore_IMF_Context *ctx,
435 Eina_List **attr __UNUSED__,
438 IBusIMContext *ibusimcontext = (IBusIMContext*)ecore_imf_context_data_get(ctx);
439 EINA_SAFETY_ON_NULL_RETURN(ibusimcontext);
441 if (ibusimcontext->enable && ibusimcontext->preedit_visible)
444 *str = strdup(ibusimcontext->preedit_string ? ibusimcontext->preedit_string: "");
447 *cursor_pos = ibusimcontext->preedit_cursor_pos;
457 EINA_LOG_DBG("str : %s, cursor_pos : %d", *str, *cursor_pos);
461 ibus_im_context_client_window_set(Ecore_IMF_Context *ctx, void *window)
463 EINA_LOG_DBG("canvas : %p", window);
464 IBusIMContext *ibusimcontext = (IBusIMContext *)ecore_imf_context_data_get(ctx);
465 EINA_SAFETY_ON_NULL_RETURN(ibusimcontext);
468 ibusimcontext->client_window = (Ecore_X_Window)(Ecore_Window)window;
472 ibus_im_context_client_canvas_set(Ecore_IMF_Context *ctx, void *canvas)
474 EINA_LOG_DBG("canvas : %p", canvas);
475 IBusIMContext *ibusimcontext = (IBusIMContext *)ecore_imf_context_data_get(ctx);
476 EINA_SAFETY_ON_NULL_RETURN(ibusimcontext);
479 ibusimcontext->client_canvas = canvas;
483 _set_cursor_location_internal(Ecore_IMF_Context *ctx)
485 IBusIMContext *ibusimcontext = (IBusIMContext *)ecore_imf_context_data_get(ctx);
487 int canvas_x, canvas_y;
489 EINA_SAFETY_ON_NULL_RETURN(ibusimcontext);
491 if (ibusimcontext->ibuscontext == NULL)
494 if (ibusimcontext->client_canvas)
496 ee = ecore_evas_ecore_evas_get(ibusimcontext->client_canvas);
499 ecore_evas_geometry_get(ee, &canvas_x, &canvas_y, NULL, NULL);
503 if (ibusimcontext->client_window)
504 _window_to_screen_geometry_get(ibusimcontext->client_window, &canvas_x, &canvas_y);
509 ibus_input_context_set_cursor_location(ibusimcontext->ibuscontext,
510 ibusimcontext->cursor_x + canvas_x,
511 ibusimcontext->cursor_y + canvas_y,
512 ibusimcontext->cursor_w,
513 ibusimcontext->cursor_h);
517 ibus_im_context_cursor_location_set(Ecore_IMF_Context *ctx, int x, int y, int w, int h)
519 EINA_LOG_DBG("x : %d, y : %d, w, %d, h :%d", x, y, w, h);
520 IBusIMContext *ibusimcontext = (IBusIMContext *)ecore_imf_context_data_get(ctx);
521 EINA_SAFETY_ON_NULL_RETURN(ibusimcontext);
523 if (ibusimcontext->cursor_x != x ||
524 ibusimcontext->cursor_y != y ||
525 ibusimcontext->cursor_w != w ||
526 ibusimcontext->cursor_h != h)
528 ibusimcontext->cursor_x = x;
529 ibusimcontext->cursor_y = y;
530 ibusimcontext->cursor_w = w;
531 ibusimcontext->cursor_h = h;
533 _set_cursor_location_internal(ctx);
538 ibus_im_context_use_preedit_set(Ecore_IMF_Context *ctx, Eina_Bool use_preedit)
540 EINA_LOG_DBG("preedit : %d", use_preedit);
541 IBusIMContext *ibusimcontext = (IBusIMContext *)ecore_imf_context_data_get(ctx);
542 EINA_SAFETY_ON_NULL_RETURN(ibusimcontext);
544 if (ibusimcontext->ibuscontext)
547 ibusimcontext->caps |= IBUS_CAP_PREEDIT_TEXT;
549 ibusimcontext->caps &= ~IBUS_CAP_PREEDIT_TEXT;
551 ibus_input_context_set_capabilities(ibusimcontext->ibuscontext, ibusimcontext->caps);
556 _bus_connected_cb(IBusBus *bus __UNUSED__,
557 IBusIMContext *ibusimcontext)
559 EINA_LOG_DBG("ibus is connected");
562 _create_input_context(ibusimcontext);
566 _ibus_context_commit_text_cb(IBusInputContext *ibuscontext __UNUSED__,
568 IBusIMContext *ibusimcontext)
570 if (!ibusimcontext || !text) return;
571 char *commit_str = text->text ? text->text : "";
573 EINA_LOG_DBG("commit string : %s", commit_str);
575 if (ibusimcontext->ctx)
577 ecore_imf_context_commit_event_add(ibusimcontext->ctx, text->text);
578 ecore_imf_context_event_callback_call(ibusimcontext->ctx, ECORE_IMF_CALLBACK_COMMIT, (void *)commit_str);
582 static XKeyEvent createXKeyEvent(Window win, Eina_Bool press, int keysym, int modifiers)
585 Display *display = ecore_x_display_get();
587 event.display = display;
589 event.root = ecore_x_window_root_get(win);
590 event.subwindow = None;
596 event.same_screen = EINA_TRUE;
597 event.state = modifiers;
598 event.keycode = XKeysymToKeycode(display, keysym);
600 event.type = KeyPress;
602 event.type = KeyRelease;
603 event.send_event = EINA_FALSE;
610 _ibus_context_forward_key_event_cb(IBusInputContext *ibuscontext __UNUSED__,
613 IBusIMContext *ibusimcontext __UNUSED__)
615 EINA_LOG_DBG("keyval : %d, state : %d", keyval, state);
617 key_event_put(keyval, state);
621 _ibus_context_update_preedit_text_cb(IBusInputContext *ibuscontext __UNUSED__,
625 IBusIMContext *ibusimcontext)
627 if (!ibusimcontext || !text) return;
632 if (ibusimcontext->preedit_string)
633 free (ibusimcontext->preedit_string);
638 ibusimcontext->preedit_string = strdup(str);
640 ibusimcontext->preedit_string = strdup("");
642 ibusimcontext->preedit_cursor_pos = cursor_pos;
644 EINA_LOG_DBG("string : %s, cursor : %d", ibusimcontext->preedit_string, ibusimcontext->preedit_cursor_pos);
646 flag = ibusimcontext->preedit_visible != visible;
647 ibusimcontext->preedit_visible = visible;
649 if (ibusimcontext->preedit_visible)
653 ecore_imf_context_preedit_start_event_add(ibusimcontext->ctx);
654 ecore_imf_context_event_callback_call(ibusimcontext->ctx, ECORE_IMF_CALLBACK_PREEDIT_START, NULL);
657 ecore_imf_context_preedit_changed_event_add(ibusimcontext->ctx);
658 ecore_imf_context_event_callback_call(ibusimcontext->ctx, ECORE_IMF_CALLBACK_PREEDIT_CHANGED, NULL);
664 ecore_imf_context_preedit_changed_event_add(ibusimcontext->ctx);
665 ecore_imf_context_event_callback_call(ibusimcontext->ctx, ECORE_IMF_CALLBACK_PREEDIT_CHANGED, NULL);
668 ecore_imf_context_preedit_end_event_add(ibusimcontext->ctx);
669 ecore_imf_context_event_callback_call(ibusimcontext->ctx, ECORE_IMF_CALLBACK_PREEDIT_END, NULL);
674 _ibus_context_show_preedit_text_cb(IBusInputContext *ibuscontext __UNUSED__,
675 IBusIMContext *ibusimcontext)
677 EINA_LOG_DBG("preedit visible : %d", ibusimcontext->preedit_visible);
678 EINA_SAFETY_ON_NULL_RETURN(ibusimcontext);
680 if (ibusimcontext->preedit_visible == EINA_TRUE)
683 ibusimcontext->preedit_visible = EINA_TRUE;
685 // call preedit start
686 ecore_imf_context_preedit_start_event_add(ibusimcontext->ctx);
687 ecore_imf_context_event_callback_call(ibusimcontext->ctx, ECORE_IMF_CALLBACK_PREEDIT_START, NULL);
689 // call preedit changed
690 ecore_imf_context_preedit_changed_event_add(ibusimcontext->ctx);
691 ecore_imf_context_event_callback_call(ibusimcontext->ctx, ECORE_IMF_CALLBACK_PREEDIT_CHANGED, NULL);
695 _ibus_context_hide_preedit_text_cb(IBusInputContext *ibuscontext __UNUSED__,
696 IBusIMContext *ibusimcontext)
698 EINA_LOG_DBG("%s", __FUNCTION__);
699 EINA_SAFETY_ON_NULL_RETURN(ibusimcontext);
701 if (ibusimcontext->preedit_visible == EINA_FALSE)
704 ibusimcontext->preedit_visible = EINA_FALSE;
706 // call preedit changed
707 ecore_imf_context_preedit_changed_event_add(ibusimcontext->ctx);
708 ecore_imf_context_event_callback_call(ibusimcontext->ctx, ECORE_IMF_CALLBACK_PREEDIT_CHANGED, NULL);
711 ecore_imf_context_preedit_end_event_add(ibusimcontext->ctx);
712 ecore_imf_context_event_callback_call(ibusimcontext->ctx, ECORE_IMF_CALLBACK_PREEDIT_END, NULL);
716 _ibus_context_enabled_cb(IBusInputContext *ibuscontext __UNUSED__,
717 IBusIMContext *ibusimcontext)
719 EINA_LOG_DBG("%s", __FUNCTION__);
720 EINA_SAFETY_ON_NULL_RETURN(ibusimcontext);
722 ibusimcontext->enable = EINA_TRUE;
726 _ibus_context_disabled_cb(IBusInputContext *ibuscontext __UNUSED__,
727 IBusIMContext *ibusimcontext)
729 EINA_LOG_DBG("%s", __FUNCTION__);
730 EINA_SAFETY_ON_NULL_RETURN(ibusimcontext);
732 ibusimcontext->enable = EINA_FALSE;
735 ibusimcontext->preedit_visible = EINA_FALSE;
736 ibusimcontext->preedit_cursor_pos = 0;
737 free (ibusimcontext->preedit_string);
738 ibusimcontext->preedit_string = NULL;
740 // call preedit changed
741 ecore_imf_context_preedit_changed_event_add(ibusimcontext->ctx);
742 ecore_imf_context_event_callback_call(ibusimcontext->ctx, ECORE_IMF_CALLBACK_PREEDIT_CHANGED, NULL);
745 ecore_imf_context_preedit_end_event_add(ibusimcontext->ctx);
746 ecore_imf_context_event_callback_call(ibusimcontext->ctx, ECORE_IMF_CALLBACK_PREEDIT_END, NULL);
750 _ibus_context_destroy_cb(IBusInputContext *ibuscontext __UNUSED__,
751 IBusIMContext *ibusimcontext)
753 EINA_LOG_DBG("%s", __FUNCTION__);
754 EINA_SAFETY_ON_NULL_RETURN(ibusimcontext);
756 ibusimcontext->ibuscontext = NULL;
757 ibusimcontext->enable = EINA_FALSE;
760 ibusimcontext->preedit_visible = EINA_FALSE;
761 ibusimcontext->preedit_cursor_pos = 0;
762 free (ibusimcontext->preedit_string);
763 ibusimcontext->preedit_string = NULL;
765 // call preedit changed
766 ecore_imf_context_preedit_changed_event_add(ibusimcontext->ctx);
767 ecore_imf_context_event_callback_call(ibusimcontext->ctx, ECORE_IMF_CALLBACK_PREEDIT_CHANGED, NULL);
770 ecore_imf_context_preedit_end_event_add(ibusimcontext->ctx);
771 ecore_imf_context_event_callback_call(ibusimcontext->ctx, ECORE_IMF_CALLBACK_PREEDIT_END, NULL);
775 _create_input_context(IBusIMContext *ibusimcontext)
777 EINA_LOG_DBG("%s", __FUNCTION__);
778 EINA_SAFETY_ON_NULL_RETURN(ibusimcontext);
780 ibusimcontext->ibuscontext = ibus_bus_create_input_context(_bus, "ecore");
782 g_return_if_fail(ibusimcontext->ibuscontext != NULL);
784 g_signal_connect(ibusimcontext->ibuscontext,
786 G_CALLBACK (_ibus_context_commit_text_cb),
788 g_signal_connect(ibusimcontext->ibuscontext,
790 G_CALLBACK (_ibus_context_forward_key_event_cb),
792 g_signal_connect(ibusimcontext->ibuscontext,
793 "update-preedit-text",
794 G_CALLBACK (_ibus_context_update_preedit_text_cb),
796 g_signal_connect(ibusimcontext->ibuscontext,
798 G_CALLBACK (_ibus_context_show_preedit_text_cb),
800 g_signal_connect(ibusimcontext->ibuscontext,
802 G_CALLBACK (_ibus_context_hide_preedit_text_cb),
804 g_signal_connect(ibusimcontext->ibuscontext,
806 G_CALLBACK (_ibus_context_enabled_cb),
808 g_signal_connect(ibusimcontext->ibuscontext,
810 G_CALLBACK (_ibus_context_disabled_cb),
812 g_signal_connect(ibusimcontext->ibuscontext, "destroy",
813 G_CALLBACK (_ibus_context_destroy_cb),
816 ibus_input_context_set_capabilities(ibusimcontext->ibuscontext, ibusimcontext->caps);
818 if (ibusimcontext->has_focus)
819 ibus_input_context_focus_in(ibusimcontext->ibuscontext);