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);
260 if (_focus_im_context == ctx)
261 _focus_im_context = NULL;
265 ibus_im_context_filter_event(Ecore_IMF_Context *ctx, Ecore_IMF_Event_Type type, Ecore_IMF_Event *event)
267 IBusIMContext *ibusimcontext = (IBusIMContext*)ecore_imf_context_data_get(ctx);
268 EINA_SAFETY_ON_NULL_RETURN_VAL(ibusimcontext, EINA_FALSE);
270 if (type != ECORE_IMF_EVENT_KEY_UP && type != ECORE_IMF_EVENT_KEY_DOWN)
273 EINA_LOG_DBG("%s", __FUNCTION__);
275 if (G_LIKELY(ibusimcontext->ibuscontext && ibusimcontext->has_focus))
277 /* If context does not have focus, ibus will process key event in sync mode.
278 * It is a workaround for increase search in treeview.
280 Eina_Bool retval = EINA_FALSE;
283 unsigned int state = 0;
285 if (type == ECORE_IMF_EVENT_KEY_UP)
287 Ecore_IMF_Event_Key_Up *ev = (Ecore_IMF_Event_Key_Up *)event;
288 if (ev->timestamp == 0)
291 keycode = ecore_x_keysym_keycode_get(ev->key);
292 keysym = XStringToKeysym(ev->key);
293 state = _ecore_imf_modifier_to_ibus_modifier(ev->modifiers) | IBUS_RELEASE_MASK;
297 retval = ibus_input_context_process_key_event(ibusimcontext->ibuscontext,
304 ibus_input_context_process_key_event_async(ibusimcontext->ibuscontext,
310 _process_key_event_done,
311 key_event_copy(keysym, state));
315 else if (type == ECORE_IMF_EVENT_KEY_DOWN)
317 Ecore_IMF_Event_Key_Down *ev = (Ecore_IMF_Event_Key_Down *)event;
318 if (ev->timestamp == 0)
321 keycode = ecore_x_keysym_keycode_get(ev->key);
322 keysym = XStringToKeysym(ev->key);
323 state = _ecore_imf_modifier_to_ibus_modifier(ev->modifiers);
326 retval = ibus_input_context_process_key_event(ibusimcontext->ibuscontext,
333 ibus_input_context_process_key_event_async(ibusimcontext->ibuscontext,
339 _process_key_event_done,
340 key_event_copy(keysym, state));
357 ibus_im_context_focus_in(Ecore_IMF_Context *ctx)
359 EINA_LOG_DBG("ctx : %p", ctx);
361 IBusIMContext *ibusimcontext = (IBusIMContext*)ecore_imf_context_data_get(ctx);
362 EINA_SAFETY_ON_NULL_RETURN(ibusimcontext);
364 if (ibusimcontext->has_focus)
367 if (_focus_im_context != NULL)
368 ecore_imf_context_focus_out(_focus_im_context);
370 ibusimcontext->has_focus = EINA_TRUE;
371 if (ibusimcontext->ibuscontext)
372 ibus_input_context_focus_in(ibusimcontext->ibuscontext);
374 if (_focus_im_context != ctx)
375 _focus_im_context = ctx;
379 ibus_im_context_focus_out(Ecore_IMF_Context *ctx)
381 EINA_LOG_DBG("ctx : %p", ctx);
383 IBusIMContext *ibusimcontext = (IBusIMContext *)ecore_imf_context_data_get(ctx);
384 EINA_SAFETY_ON_NULL_RETURN(ibusimcontext);
386 if (ibusimcontext->has_focus == EINA_FALSE)
389 if (_focus_im_context == ctx)
390 _focus_im_context = NULL;
392 ibusimcontext->has_focus = EINA_FALSE;
393 if (ibusimcontext->ibuscontext)
394 ibus_input_context_focus_out(ibusimcontext->ibuscontext);
398 ibus_im_context_reset(Ecore_IMF_Context *ctx)
400 IBusIMContext *ibusimcontext = (IBusIMContext*)ecore_imf_context_data_get(ctx);
401 EINA_SAFETY_ON_NULL_RETURN(ibusimcontext);
403 if (ibusimcontext->ibuscontext)
404 ibus_input_context_reset(ibusimcontext->ibuscontext);
408 ibus_im_context_preedit_string_get(Ecore_IMF_Context *ctx,
412 IBusIMContext *ibusimcontext = (IBusIMContext*)ecore_imf_context_data_get(ctx);
413 EINA_SAFETY_ON_NULL_RETURN(ibusimcontext);
415 if (ibusimcontext->enable && ibusimcontext->preedit_visible)
418 *str = strdup(ibusimcontext->preedit_string ? ibusimcontext->preedit_string: "");
421 *cursor_pos = ibusimcontext->preedit_cursor_pos;
431 EINA_LOG_DBG("str : %s, cursor_pos : %d", *str, *cursor_pos);
435 ibus_im_context_preedit_string_with_attributes_get(Ecore_IMF_Context *ctx,
437 Eina_List **attr __UNUSED__,
440 IBusIMContext *ibusimcontext = (IBusIMContext*)ecore_imf_context_data_get(ctx);
441 EINA_SAFETY_ON_NULL_RETURN(ibusimcontext);
443 if (ibusimcontext->enable && ibusimcontext->preedit_visible)
446 *str = strdup(ibusimcontext->preedit_string ? ibusimcontext->preedit_string: "");
449 *cursor_pos = ibusimcontext->preedit_cursor_pos;
459 EINA_LOG_DBG("str : %s, cursor_pos : %d", *str, *cursor_pos);
463 ibus_im_context_client_window_set(Ecore_IMF_Context *ctx, void *window)
465 EINA_LOG_DBG("canvas : %p", window);
466 IBusIMContext *ibusimcontext = (IBusIMContext *)ecore_imf_context_data_get(ctx);
467 EINA_SAFETY_ON_NULL_RETURN(ibusimcontext);
470 ibusimcontext->client_window = (Ecore_X_Window)(Ecore_Window)window;
474 ibus_im_context_client_canvas_set(Ecore_IMF_Context *ctx, void *canvas)
476 EINA_LOG_DBG("canvas : %p", canvas);
477 IBusIMContext *ibusimcontext = (IBusIMContext *)ecore_imf_context_data_get(ctx);
478 EINA_SAFETY_ON_NULL_RETURN(ibusimcontext);
481 ibusimcontext->client_canvas = canvas;
485 _set_cursor_location_internal(Ecore_IMF_Context *ctx)
487 IBusIMContext *ibusimcontext = (IBusIMContext *)ecore_imf_context_data_get(ctx);
489 int canvas_x, canvas_y;
491 EINA_SAFETY_ON_NULL_RETURN(ibusimcontext);
493 if (ibusimcontext->ibuscontext == NULL)
496 if (ibusimcontext->client_canvas)
498 ee = ecore_evas_ecore_evas_get(ibusimcontext->client_canvas);
501 ecore_evas_geometry_get(ee, &canvas_x, &canvas_y, NULL, NULL);
505 if (ibusimcontext->client_window)
506 _window_to_screen_geometry_get(ibusimcontext->client_window, &canvas_x, &canvas_y);
511 ibus_input_context_set_cursor_location(ibusimcontext->ibuscontext,
512 ibusimcontext->cursor_x + canvas_x,
513 ibusimcontext->cursor_y + canvas_y,
514 ibusimcontext->cursor_w,
515 ibusimcontext->cursor_h);
519 ibus_im_context_cursor_location_set(Ecore_IMF_Context *ctx, int x, int y, int w, int h)
521 EINA_LOG_DBG("x : %d, y : %d, w, %d, h :%d", x, y, w, h);
522 IBusIMContext *ibusimcontext = (IBusIMContext *)ecore_imf_context_data_get(ctx);
523 EINA_SAFETY_ON_NULL_RETURN(ibusimcontext);
525 if (ibusimcontext->cursor_x != x ||
526 ibusimcontext->cursor_y != y ||
527 ibusimcontext->cursor_w != w ||
528 ibusimcontext->cursor_h != h)
530 ibusimcontext->cursor_x = x;
531 ibusimcontext->cursor_y = y;
532 ibusimcontext->cursor_w = w;
533 ibusimcontext->cursor_h = h;
535 _set_cursor_location_internal(ctx);
540 ibus_im_context_use_preedit_set(Ecore_IMF_Context *ctx, Eina_Bool use_preedit)
542 EINA_LOG_DBG("preedit : %d", use_preedit);
543 IBusIMContext *ibusimcontext = (IBusIMContext *)ecore_imf_context_data_get(ctx);
544 EINA_SAFETY_ON_NULL_RETURN(ibusimcontext);
546 if (ibusimcontext->ibuscontext)
549 ibusimcontext->caps |= IBUS_CAP_PREEDIT_TEXT;
551 ibusimcontext->caps &= ~IBUS_CAP_PREEDIT_TEXT;
553 ibus_input_context_set_capabilities(ibusimcontext->ibuscontext, ibusimcontext->caps);
558 _bus_connected_cb(IBusBus *bus __UNUSED__,
559 IBusIMContext *ibusimcontext)
561 EINA_LOG_DBG("ibus is connected");
564 _create_input_context(ibusimcontext);
568 _ibus_context_commit_text_cb(IBusInputContext *ibuscontext __UNUSED__,
570 IBusIMContext *ibusimcontext)
572 if (!ibusimcontext || !text) return;
573 char *commit_str = text->text ? text->text : "";
575 EINA_LOG_DBG("commit string : %s", commit_str);
577 if (ibusimcontext->ctx)
579 ecore_imf_context_commit_event_add(ibusimcontext->ctx, text->text);
580 ecore_imf_context_event_callback_call(ibusimcontext->ctx, ECORE_IMF_CALLBACK_COMMIT, (void *)commit_str);
584 static XKeyEvent createXKeyEvent(Window win, Eina_Bool press, int keysym, int modifiers)
587 Display *display = ecore_x_display_get();
589 event.display = display;
591 event.root = ecore_x_window_root_get(win);
592 event.subwindow = None;
598 event.same_screen = EINA_TRUE;
599 event.state = modifiers;
600 event.keycode = XKeysymToKeycode(display, keysym);
602 event.type = KeyPress;
604 event.type = KeyRelease;
605 event.send_event = EINA_FALSE;
612 _ibus_context_forward_key_event_cb(IBusInputContext *ibuscontext __UNUSED__,
615 IBusIMContext *ibusimcontext __UNUSED__)
617 EINA_LOG_DBG("keyval : %d, state : %d", keyval, state);
619 key_event_put(keyval, state);
623 _ibus_context_update_preedit_text_cb(IBusInputContext *ibuscontext __UNUSED__,
627 IBusIMContext *ibusimcontext)
629 if (!ibusimcontext || !text) return;
634 if (ibusimcontext->preedit_string)
635 free (ibusimcontext->preedit_string);
640 ibusimcontext->preedit_string = strdup(str);
642 ibusimcontext->preedit_string = strdup("");
644 ibusimcontext->preedit_cursor_pos = cursor_pos;
646 EINA_LOG_DBG("string : %s, cursor : %d", ibusimcontext->preedit_string, ibusimcontext->preedit_cursor_pos);
648 flag = ibusimcontext->preedit_visible != visible;
649 ibusimcontext->preedit_visible = visible;
651 if (ibusimcontext->preedit_visible)
655 ecore_imf_context_preedit_start_event_add(ibusimcontext->ctx);
656 ecore_imf_context_event_callback_call(ibusimcontext->ctx, ECORE_IMF_CALLBACK_PREEDIT_START, NULL);
659 ecore_imf_context_preedit_changed_event_add(ibusimcontext->ctx);
660 ecore_imf_context_event_callback_call(ibusimcontext->ctx, ECORE_IMF_CALLBACK_PREEDIT_CHANGED, NULL);
666 ecore_imf_context_preedit_changed_event_add(ibusimcontext->ctx);
667 ecore_imf_context_event_callback_call(ibusimcontext->ctx, ECORE_IMF_CALLBACK_PREEDIT_CHANGED, NULL);
670 ecore_imf_context_preedit_end_event_add(ibusimcontext->ctx);
671 ecore_imf_context_event_callback_call(ibusimcontext->ctx, ECORE_IMF_CALLBACK_PREEDIT_END, NULL);
676 _ibus_context_show_preedit_text_cb(IBusInputContext *ibuscontext __UNUSED__,
677 IBusIMContext *ibusimcontext)
679 EINA_LOG_DBG("preedit visible : %d", ibusimcontext->preedit_visible);
680 EINA_SAFETY_ON_NULL_RETURN(ibusimcontext);
682 if (ibusimcontext->preedit_visible == EINA_TRUE)
685 ibusimcontext->preedit_visible = EINA_TRUE;
687 // call preedit start
688 ecore_imf_context_preedit_start_event_add(ibusimcontext->ctx);
689 ecore_imf_context_event_callback_call(ibusimcontext->ctx, ECORE_IMF_CALLBACK_PREEDIT_START, NULL);
691 // call preedit changed
692 ecore_imf_context_preedit_changed_event_add(ibusimcontext->ctx);
693 ecore_imf_context_event_callback_call(ibusimcontext->ctx, ECORE_IMF_CALLBACK_PREEDIT_CHANGED, NULL);
697 _ibus_context_hide_preedit_text_cb(IBusInputContext *ibuscontext __UNUSED__,
698 IBusIMContext *ibusimcontext)
700 EINA_LOG_DBG("%s", __FUNCTION__);
701 EINA_SAFETY_ON_NULL_RETURN(ibusimcontext);
703 if (ibusimcontext->preedit_visible == EINA_FALSE)
706 ibusimcontext->preedit_visible = EINA_FALSE;
708 // call preedit changed
709 ecore_imf_context_preedit_changed_event_add(ibusimcontext->ctx);
710 ecore_imf_context_event_callback_call(ibusimcontext->ctx, ECORE_IMF_CALLBACK_PREEDIT_CHANGED, NULL);
713 ecore_imf_context_preedit_end_event_add(ibusimcontext->ctx);
714 ecore_imf_context_event_callback_call(ibusimcontext->ctx, ECORE_IMF_CALLBACK_PREEDIT_END, NULL);
718 _ibus_context_enabled_cb(IBusInputContext *ibuscontext __UNUSED__,
719 IBusIMContext *ibusimcontext)
721 EINA_LOG_DBG("%s", __FUNCTION__);
722 EINA_SAFETY_ON_NULL_RETURN(ibusimcontext);
724 ibusimcontext->enable = EINA_TRUE;
728 _ibus_context_disabled_cb(IBusInputContext *ibuscontext __UNUSED__,
729 IBusIMContext *ibusimcontext)
731 EINA_LOG_DBG("%s", __FUNCTION__);
732 EINA_SAFETY_ON_NULL_RETURN(ibusimcontext);
734 ibusimcontext->enable = EINA_FALSE;
737 ibusimcontext->preedit_visible = EINA_FALSE;
738 ibusimcontext->preedit_cursor_pos = 0;
739 free (ibusimcontext->preedit_string);
740 ibusimcontext->preedit_string = NULL;
742 // call preedit changed
743 ecore_imf_context_preedit_changed_event_add(ibusimcontext->ctx);
744 ecore_imf_context_event_callback_call(ibusimcontext->ctx, ECORE_IMF_CALLBACK_PREEDIT_CHANGED, NULL);
747 ecore_imf_context_preedit_end_event_add(ibusimcontext->ctx);
748 ecore_imf_context_event_callback_call(ibusimcontext->ctx, ECORE_IMF_CALLBACK_PREEDIT_END, NULL);
752 _ibus_context_destroy_cb(IBusInputContext *ibuscontext __UNUSED__,
753 IBusIMContext *ibusimcontext)
755 EINA_LOG_DBG("%s", __FUNCTION__);
756 EINA_SAFETY_ON_NULL_RETURN(ibusimcontext);
758 ibusimcontext->ibuscontext = NULL;
759 ibusimcontext->enable = EINA_FALSE;
762 ibusimcontext->preedit_visible = EINA_FALSE;
763 ibusimcontext->preedit_cursor_pos = 0;
764 free (ibusimcontext->preedit_string);
765 ibusimcontext->preedit_string = NULL;
767 // call preedit changed
768 ecore_imf_context_preedit_changed_event_add(ibusimcontext->ctx);
769 ecore_imf_context_event_callback_call(ibusimcontext->ctx, ECORE_IMF_CALLBACK_PREEDIT_CHANGED, NULL);
772 ecore_imf_context_preedit_end_event_add(ibusimcontext->ctx);
773 ecore_imf_context_event_callback_call(ibusimcontext->ctx, ECORE_IMF_CALLBACK_PREEDIT_END, NULL);
777 _create_input_context(IBusIMContext *ibusimcontext)
779 EINA_LOG_DBG("%s", __FUNCTION__);
780 EINA_SAFETY_ON_NULL_RETURN(ibusimcontext);
782 ibusimcontext->ibuscontext = ibus_bus_create_input_context(_bus, "ecore");
784 g_return_if_fail(ibusimcontext->ibuscontext != NULL);
786 g_signal_connect(ibusimcontext->ibuscontext,
788 G_CALLBACK (_ibus_context_commit_text_cb),
790 g_signal_connect(ibusimcontext->ibuscontext,
792 G_CALLBACK (_ibus_context_forward_key_event_cb),
794 g_signal_connect(ibusimcontext->ibuscontext,
795 "update-preedit-text",
796 G_CALLBACK (_ibus_context_update_preedit_text_cb),
798 g_signal_connect(ibusimcontext->ibuscontext,
800 G_CALLBACK (_ibus_context_show_preedit_text_cb),
802 g_signal_connect(ibusimcontext->ibuscontext,
804 G_CALLBACK (_ibus_context_hide_preedit_text_cb),
806 g_signal_connect(ibusimcontext->ibuscontext,
808 G_CALLBACK (_ibus_context_enabled_cb),
810 g_signal_connect(ibusimcontext->ibuscontext,
812 G_CALLBACK (_ibus_context_disabled_cb),
814 g_signal_connect(ibusimcontext->ibuscontext, "destroy",
815 G_CALLBACK (_ibus_context_destroy_cb),
818 ibus_input_context_set_capabilities(ibusimcontext->ibuscontext, ibusimcontext->caps);
820 if (ibusimcontext->has_focus)
821 ibus_input_context_focus_in(ibusimcontext->ibuscontext);