forked from enlightenment/efl
1072 lines
34 KiB
C
1072 lines
34 KiB
C
#ifdef HAVE_CONFIG_H
|
|
# include <config.h>
|
|
#endif
|
|
|
|
#include <sys/types.h>
|
|
#include <sys/socket.h>
|
|
#include <sys/time.h>
|
|
#include <sys/un.h>
|
|
#include <string.h>
|
|
#include <stdlib.h>
|
|
#include <glib.h>
|
|
|
|
#include <X11/Xlib.h>
|
|
#include <Ecore_X.h>
|
|
#include <Ecore_Evas.h>
|
|
|
|
#include <ibus.h>
|
|
#include "ibus_imcontext.h"
|
|
|
|
struct _IBusIMContext
|
|
{
|
|
/* instance members */
|
|
Ecore_IMF_Context *ctx;
|
|
|
|
IBusInputContext *ibuscontext;
|
|
|
|
/* preedit status */
|
|
char *preedit_string;
|
|
Eina_List *preedit_attrs;
|
|
int preedit_cursor_pos;
|
|
Eina_Bool preedit_visible;
|
|
|
|
int cursor_x;
|
|
int cursor_y;
|
|
int cursor_w;
|
|
int cursor_h;
|
|
|
|
Eina_Bool has_focus;
|
|
|
|
Ecore_X_Window client_window;
|
|
Evas *client_canvas;
|
|
|
|
int caps;
|
|
};
|
|
|
|
typedef struct _KeyEvent KeyEvent;
|
|
|
|
struct _KeyEvent
|
|
{
|
|
int keysym;
|
|
int keycode;
|
|
int state;
|
|
};
|
|
|
|
static Eina_Bool _sync_mode_use = EINA_TRUE;
|
|
|
|
static Ecore_IMF_Context *_focus_im_context = NULL;
|
|
static IBusBus *_bus = NULL;
|
|
|
|
/* functions prototype */
|
|
/* static methods*/
|
|
static void _ecore_imf_context_ibus_create(IBusIMContext *context);
|
|
static void _ecore_imf_context_ibus_cursor_location_set(Ecore_IMF_Context *ctx);
|
|
static void _ecore_imf_context_ibus_bus_connected_cb(IBusBus *bus, IBusIMContext *context);
|
|
static XKeyEvent _ecore_imf_ibus_x_key_event_generate(Window win,
|
|
Eina_Bool press,
|
|
int keysym,
|
|
int keycode,
|
|
int modifiers);
|
|
|
|
static unsigned int
|
|
utf8_offset_to_index(const char *str, int offset)
|
|
{
|
|
int index = 0;
|
|
int i;
|
|
for (i = 0; i < offset; i++)
|
|
eina_unicode_utf8_next_get(str, &index);
|
|
|
|
return index;
|
|
}
|
|
|
|
static int
|
|
sort_cb(const void *d1, const void *d2)
|
|
{
|
|
const Ecore_IMF_Preedit_Attr *attr1 = d1;
|
|
const Ecore_IMF_Preedit_Attr *attr2 = d2;
|
|
|
|
if (!attr1) return 1;
|
|
if (!attr2) return -1;
|
|
|
|
if (attr1->start_index < attr2->start_index)
|
|
return -1;
|
|
else
|
|
return 1;
|
|
}
|
|
|
|
static void
|
|
_ecore_imf_ibus_window_to_screen_geometry_get(Ecore_X_Window client_win,
|
|
int *x,
|
|
int *y)
|
|
{
|
|
Ecore_X_Window root_window, win;
|
|
int win_x, win_y;
|
|
int sum_x = 0, sum_y = 0;
|
|
|
|
if (!ecore_x_display_get()) goto end;
|
|
|
|
root_window = ecore_x_window_root_get(client_win);
|
|
win = client_win;
|
|
|
|
while (root_window != win)
|
|
{
|
|
ecore_x_window_geometry_get(win, &win_x, &win_y, NULL, NULL);
|
|
sum_x += win_x;
|
|
sum_y += win_y;
|
|
win = ecore_x_window_parent_get(win);
|
|
}
|
|
|
|
end:
|
|
if (x)
|
|
*x = sum_x;
|
|
if (y)
|
|
*y = sum_y;
|
|
}
|
|
|
|
static unsigned int
|
|
_ecore_imf_modifier_to_ibus_modifier(unsigned int modifier)
|
|
{
|
|
unsigned int state = 0;
|
|
|
|
/**< "Control" is pressed */
|
|
if (modifier & ECORE_IMF_KEYBOARD_MODIFIER_CTRL)
|
|
state |= IBUS_CONTROL_MASK;
|
|
|
|
/**< "Alt" is pressed */
|
|
if (modifier & ECORE_IMF_KEYBOARD_MODIFIER_ALT)
|
|
state |= IBUS_MOD1_MASK;
|
|
|
|
/**< "Shift" is pressed */
|
|
if (modifier & ECORE_IMF_KEYBOARD_MODIFIER_SHIFT)
|
|
state |= IBUS_SHIFT_MASK;
|
|
|
|
/**< "Win" (between "Ctrl" and "Alt") */
|
|
if (modifier & ECORE_IMF_KEYBOARD_MODIFIER_WIN)
|
|
state |= IBUS_SUPER_MASK;
|
|
|
|
/**< "AltGr" is pressed */
|
|
if (modifier & ECORE_IMF_KEYBOARD_MODIFIER_ALTGR)
|
|
state |= IBUS_MOD5_MASK;
|
|
|
|
return state;
|
|
}
|
|
|
|
static unsigned int
|
|
_ecore_imf_locks_to_ibus_modifier(unsigned int locks)
|
|
{
|
|
unsigned int state = 0;
|
|
|
|
/**< "Num lock" is pressed */
|
|
if (locks & ECORE_IMF_KEYBOARD_LOCK_NUM)
|
|
state |= IBUS_MOD2_MASK;
|
|
|
|
/**< "Caps lock" is pressed */
|
|
if (locks & ECORE_IMF_KEYBOARD_LOCK_CAPS)
|
|
state |= IBUS_LOCK_MASK;
|
|
|
|
return state;
|
|
}
|
|
|
|
static void
|
|
_ecore_imf_ibus_key_event_put(int keysym, int keycode, int state)
|
|
{
|
|
// Find the window which has the current keyboard focus.
|
|
Window winFocus = 0;
|
|
int revert = RevertToParent;
|
|
|
|
if (!ecore_x_display_get()) return;
|
|
XGetInputFocus(ecore_x_display_get(), &winFocus, &revert);
|
|
|
|
XKeyEvent event;
|
|
if (state & IBUS_RELEASE_MASK)
|
|
{
|
|
event = _ecore_imf_ibus_x_key_event_generate(winFocus,
|
|
EINA_FALSE,
|
|
keysym,
|
|
keycode,
|
|
state);
|
|
XSendEvent(event.display, event.window, True, KeyReleaseMask, (XEvent *)&event);
|
|
}
|
|
else
|
|
{
|
|
event = _ecore_imf_ibus_x_key_event_generate(winFocus,
|
|
EINA_TRUE,
|
|
keysym,
|
|
keycode,
|
|
state);
|
|
XSendEvent(event.display, event.window, True, KeyPressMask, (XEvent *)&event);
|
|
}
|
|
}
|
|
|
|
static KeyEvent *
|
|
_ecore_imf_ibus_key_event_copy(int keysym, int keycode, int state)
|
|
{
|
|
KeyEvent *kev = calloc(1, sizeof(KeyEvent));
|
|
kev->keysym = keysym;
|
|
kev->keycode = keycode;
|
|
kev->state = state;
|
|
|
|
return kev;
|
|
}
|
|
|
|
static void
|
|
_ecore_imf_ibus_process_key_event_done(GObject *object,
|
|
GAsyncResult *res,
|
|
gpointer user_data)
|
|
{
|
|
IBusInputContext *context = (IBusInputContext *)object;
|
|
KeyEvent *event = (KeyEvent *)user_data;
|
|
|
|
GError *error = NULL;
|
|
Eina_Bool retval = ibus_input_context_process_key_event_async_finish(context,
|
|
res,
|
|
&error);
|
|
|
|
if (error != NULL)
|
|
{
|
|
g_warning("Process Key Event failed: %s.", error->message);
|
|
g_error_free(error);
|
|
}
|
|
|
|
if (retval == EINA_FALSE)
|
|
{
|
|
_ecore_imf_ibus_key_event_put(event->keysym,
|
|
event->keycode,
|
|
event->state);
|
|
}
|
|
free(event);
|
|
}
|
|
|
|
static void
|
|
_request_surrounding_text(IBusIMContext *ibusimcontext)
|
|
{
|
|
EINA_SAFETY_ON_NULL_RETURN(ibusimcontext);
|
|
EINA_SAFETY_ON_NULL_RETURN(ibusimcontext->ibuscontext);
|
|
EINA_SAFETY_ON_NULL_RETURN(ibusimcontext->ctx);
|
|
|
|
if ((ibusimcontext->caps & IBUS_CAP_SURROUNDING_TEXT) != 0 &&
|
|
ibus_input_context_needs_surrounding_text(ibusimcontext->ibuscontext))
|
|
{
|
|
char *surrounding = NULL;
|
|
int cursor_pos;
|
|
IBusText *ibustext;
|
|
|
|
EINA_LOG_DBG ("requesting surrounding text...\n");
|
|
|
|
if (ecore_imf_context_surrounding_get(ibusimcontext->ctx,
|
|
&surrounding,
|
|
&cursor_pos))
|
|
{
|
|
if (!surrounding)
|
|
return;
|
|
|
|
if (cursor_pos < 0)
|
|
{
|
|
free(surrounding);
|
|
return;
|
|
}
|
|
|
|
ibustext = ibus_text_new_from_string(surrounding);
|
|
|
|
ibus_input_context_set_surrounding_text(ibusimcontext->ibuscontext,
|
|
ibustext,
|
|
cursor_pos,
|
|
cursor_pos);
|
|
|
|
free(surrounding);
|
|
}
|
|
else
|
|
{
|
|
ibusimcontext->caps &= ~IBUS_CAP_SURROUNDING_TEXT;
|
|
ibus_input_context_set_capabilities(ibusimcontext->ibuscontext,
|
|
ibusimcontext->caps);
|
|
}
|
|
}
|
|
}
|
|
|
|
IBusIMContext *
|
|
ecore_imf_context_ibus_new(void)
|
|
{
|
|
EINA_LOG_DBG("%s", __func__);
|
|
|
|
IBusIMContext *context = calloc(1, sizeof(IBusIMContext));
|
|
|
|
/* init bus object */
|
|
if (_bus == NULL)
|
|
{
|
|
char *display_name = NULL;
|
|
|
|
if ((display_name = getenv("DISPLAY")))
|
|
ibus_set_display(display_name);
|
|
else
|
|
ibus_set_display(":0.0");
|
|
|
|
_bus = ibus_bus_new();
|
|
}
|
|
|
|
return context;
|
|
}
|
|
|
|
void
|
|
ecore_imf_context_ibus_shutdown(void)
|
|
{
|
|
if (_bus)
|
|
{
|
|
g_object_unref(_bus);
|
|
_bus = NULL;
|
|
}
|
|
}
|
|
|
|
void
|
|
ecore_imf_context_ibus_add(Ecore_IMF_Context *ctx)
|
|
{
|
|
EINA_LOG_DBG("%s", __func__);
|
|
|
|
char *s = NULL;
|
|
IBusIMContext *ibusimcontext = (IBusIMContext *)ecore_imf_context_data_get(ctx);
|
|
EINA_SAFETY_ON_NULL_RETURN(ibusimcontext);
|
|
|
|
ibusimcontext->client_window = 0;
|
|
|
|
// Init preedit status
|
|
ibusimcontext->preedit_string = NULL;
|
|
ibusimcontext->preedit_attrs = NULL;
|
|
ibusimcontext->preedit_cursor_pos = 0;
|
|
ibusimcontext->preedit_visible = EINA_FALSE;
|
|
|
|
// Init cursor area
|
|
ibusimcontext->cursor_x = -1;
|
|
ibusimcontext->cursor_y = -1;
|
|
ibusimcontext->cursor_w = 0;
|
|
ibusimcontext->cursor_h = 0;
|
|
|
|
ibusimcontext->ibuscontext = NULL;
|
|
ibusimcontext->has_focus = EINA_FALSE;
|
|
ibusimcontext->caps = IBUS_CAP_PREEDIT_TEXT | IBUS_CAP_FOCUS | IBUS_CAP_SURROUNDING_TEXT;;
|
|
ibusimcontext->ctx = ctx;
|
|
|
|
s = getenv("IBUS_ENABLE_SYNC_MODE");
|
|
if (s)
|
|
_sync_mode_use = !!atoi(s);
|
|
|
|
if (ibus_bus_is_connected(_bus))
|
|
_ecore_imf_context_ibus_create(ibusimcontext);
|
|
|
|
g_signal_connect(_bus, "connected", G_CALLBACK (_ecore_imf_context_ibus_bus_connected_cb), ibusimcontext);
|
|
}
|
|
|
|
void
|
|
ecore_imf_context_ibus_del(Ecore_IMF_Context *ctx)
|
|
{
|
|
EINA_LOG_DBG("%s", __func__);
|
|
|
|
IBusIMContext *ibusimcontext = (IBusIMContext*)ecore_imf_context_data_get(ctx);
|
|
Ecore_IMF_Preedit_Attr *attr = NULL;
|
|
|
|
EINA_SAFETY_ON_NULL_RETURN(ibusimcontext);
|
|
|
|
g_signal_handlers_disconnect_by_func(_bus,
|
|
G_CALLBACK(_ecore_imf_context_ibus_bus_connected_cb),
|
|
ibusimcontext);
|
|
|
|
if (ibusimcontext->ibuscontext)
|
|
ibus_proxy_destroy((IBusProxy *)ibusimcontext->ibuscontext);
|
|
|
|
// release preedit
|
|
if (ibusimcontext->preedit_string)
|
|
free(ibusimcontext->preedit_string);
|
|
ibusimcontext->preedit_string = NULL;
|
|
|
|
if (ibusimcontext->preedit_attrs)
|
|
{
|
|
EINA_LIST_FREE(ibusimcontext->preedit_attrs, attr)
|
|
free(attr);
|
|
}
|
|
|
|
if (_focus_im_context == ctx)
|
|
_focus_im_context = NULL;
|
|
|
|
free(ibusimcontext);
|
|
}
|
|
|
|
Eina_Bool
|
|
ecore_imf_context_ibus_filter_event(Ecore_IMF_Context *ctx,
|
|
Ecore_IMF_Event_Type type,
|
|
Ecore_IMF_Event *event)
|
|
{
|
|
IBusIMContext *ibusimcontext = (IBusIMContext*)ecore_imf_context_data_get(ctx);
|
|
EINA_SAFETY_ON_NULL_RETURN_VAL(ibusimcontext, EINA_FALSE);
|
|
|
|
if (!ecore_x_display_get()) return EINA_FALSE;
|
|
if (type != ECORE_IMF_EVENT_KEY_UP && type != ECORE_IMF_EVENT_KEY_DOWN)
|
|
return EINA_FALSE;
|
|
|
|
EINA_LOG_DBG("%s", __func__);
|
|
|
|
if (G_LIKELY(ibusimcontext->ibuscontext && ibusimcontext->has_focus))
|
|
{
|
|
/* If context does not have focus, ibus will process key event in sync mode.
|
|
* It is a workaround for increase search in treeview.
|
|
*/
|
|
Eina_Bool retval = EINA_FALSE;
|
|
int keycode;
|
|
int keysym;
|
|
unsigned int state = 0;
|
|
|
|
if (type == ECORE_IMF_EVENT_KEY_UP)
|
|
{
|
|
Ecore_IMF_Event_Key_Up *ev = (Ecore_IMF_Event_Key_Up *)event;
|
|
if (ev->timestamp == 0)
|
|
return EINA_FALSE;
|
|
|
|
keycode = ecore_x_keysym_keycode_get(ev->keyname);
|
|
keysym = XStringToKeysym(ev->key);
|
|
state = _ecore_imf_modifier_to_ibus_modifier(ev->modifiers) |
|
|
_ecore_imf_locks_to_ibus_modifier(ev->locks) | IBUS_RELEASE_MASK;
|
|
|
|
if (_sync_mode_use)
|
|
{
|
|
retval = ibus_input_context_process_key_event(ibusimcontext->ibuscontext,
|
|
keysym,
|
|
keycode - 8,
|
|
state);
|
|
}
|
|
else
|
|
{
|
|
ibus_input_context_process_key_event_async(ibusimcontext->ibuscontext,
|
|
keysym,
|
|
keycode - 8,
|
|
state,
|
|
-1,
|
|
NULL,
|
|
_ecore_imf_ibus_process_key_event_done,
|
|
_ecore_imf_ibus_key_event_copy(keysym, keycode, state));
|
|
retval = EINA_TRUE;
|
|
}
|
|
}
|
|
else if (type == ECORE_IMF_EVENT_KEY_DOWN)
|
|
{
|
|
Ecore_IMF_Event_Key_Down *ev = (Ecore_IMF_Event_Key_Down *)event;
|
|
if (ev->timestamp == 0)
|
|
return EINA_FALSE;
|
|
|
|
_request_surrounding_text(ibusimcontext);
|
|
|
|
keycode = ecore_x_keysym_keycode_get(ev->keyname);
|
|
keysym = XStringToKeysym(ev->key);
|
|
state = _ecore_imf_modifier_to_ibus_modifier(ev->modifiers) |
|
|
_ecore_imf_locks_to_ibus_modifier(ev->locks);
|
|
|
|
if (_sync_mode_use)
|
|
{
|
|
retval = ibus_input_context_process_key_event(ibusimcontext->ibuscontext,
|
|
keysym,
|
|
keycode - 8,
|
|
state);
|
|
}
|
|
else
|
|
{
|
|
ibus_input_context_process_key_event_async(ibusimcontext->ibuscontext,
|
|
keysym,
|
|
keycode - 8,
|
|
state,
|
|
-1,
|
|
NULL,
|
|
_ecore_imf_ibus_process_key_event_done,
|
|
_ecore_imf_ibus_key_event_copy(keysym, keycode, state));
|
|
retval = EINA_TRUE;
|
|
}
|
|
}
|
|
|
|
if (retval)
|
|
return EINA_TRUE;
|
|
else
|
|
return EINA_FALSE;
|
|
}
|
|
else
|
|
return EINA_FALSE;
|
|
}
|
|
|
|
void
|
|
ecore_imf_context_ibus_focus_in(Ecore_IMF_Context *ctx)
|
|
{
|
|
EINA_LOG_DBG("ctx : %p", ctx);
|
|
|
|
IBusIMContext *ibusimcontext = (IBusIMContext*)ecore_imf_context_data_get(ctx);
|
|
EINA_SAFETY_ON_NULL_RETURN(ibusimcontext);
|
|
|
|
if (ibusimcontext->has_focus)
|
|
return;
|
|
|
|
if (_focus_im_context != NULL)
|
|
ecore_imf_context_focus_out(_focus_im_context);
|
|
|
|
ibusimcontext->has_focus = EINA_TRUE;
|
|
if (ibusimcontext->ibuscontext)
|
|
ibus_input_context_focus_in(ibusimcontext->ibuscontext);
|
|
|
|
_request_surrounding_text(ibusimcontext);
|
|
|
|
if (_focus_im_context != ctx)
|
|
_focus_im_context = ctx;
|
|
}
|
|
|
|
void
|
|
ecore_imf_context_ibus_focus_out(Ecore_IMF_Context *ctx)
|
|
{
|
|
EINA_LOG_DBG("ctx : %p", ctx);
|
|
|
|
IBusIMContext *ibusimcontext = (IBusIMContext *)ecore_imf_context_data_get(ctx);
|
|
EINA_SAFETY_ON_NULL_RETURN(ibusimcontext);
|
|
|
|
if (ibusimcontext->has_focus == EINA_FALSE)
|
|
return;
|
|
|
|
if (_focus_im_context == ctx)
|
|
_focus_im_context = NULL;
|
|
|
|
ibusimcontext->has_focus = EINA_FALSE;
|
|
if (ibusimcontext->ibuscontext)
|
|
ibus_input_context_focus_out(ibusimcontext->ibuscontext);
|
|
}
|
|
|
|
void
|
|
ecore_imf_context_ibus_reset(Ecore_IMF_Context *ctx)
|
|
{
|
|
IBusIMContext *ibusimcontext = (IBusIMContext*)ecore_imf_context_data_get(ctx);
|
|
EINA_SAFETY_ON_NULL_RETURN(ibusimcontext);
|
|
|
|
if (ibusimcontext->ibuscontext)
|
|
ibus_input_context_reset(ibusimcontext->ibuscontext);
|
|
}
|
|
|
|
void
|
|
ecore_imf_context_ibus_preedit_string_get(Ecore_IMF_Context *ctx,
|
|
char **str,
|
|
int *cursor_pos)
|
|
{
|
|
IBusIMContext *ibusimcontext = (IBusIMContext*)ecore_imf_context_data_get(ctx);
|
|
EINA_SAFETY_ON_NULL_RETURN(ibusimcontext);
|
|
|
|
if (ibusimcontext->preedit_visible)
|
|
{
|
|
if (str)
|
|
*str = strdup(ibusimcontext->preedit_string ? ibusimcontext->preedit_string: "");
|
|
|
|
if (cursor_pos)
|
|
*cursor_pos = ibusimcontext->preedit_cursor_pos;
|
|
}
|
|
else
|
|
{
|
|
if (str)
|
|
*str = strdup("");
|
|
|
|
if (cursor_pos)
|
|
*cursor_pos = 0;
|
|
}
|
|
|
|
if (str)
|
|
EINA_LOG_DBG("str : %s", *str);
|
|
|
|
if (cursor_pos)
|
|
EINA_LOG_DBG("cursor_pos : %d", *cursor_pos);
|
|
}
|
|
|
|
void
|
|
ecore_imf_context_ibus_preedit_string_with_attributes_get(Ecore_IMF_Context *ctx,
|
|
char **str,
|
|
Eina_List **attrs,
|
|
int *cursor_pos)
|
|
{
|
|
IBusIMContext *ibusimcontext = (IBusIMContext*)ecore_imf_context_data_get(ctx);
|
|
EINA_SAFETY_ON_NULL_RETURN(ibusimcontext);
|
|
Eina_List *l;
|
|
Ecore_IMF_Preedit_Attr *attr1 = NULL, *attr2 = NULL;
|
|
|
|
ecore_imf_context_ibus_preedit_string_get(ctx, str, cursor_pos);
|
|
|
|
if (attrs)
|
|
{
|
|
if (ibusimcontext->preedit_attrs)
|
|
{
|
|
EINA_LIST_FOREACH(ibusimcontext->preedit_attrs, l, attr1)
|
|
{
|
|
attr2 = (Ecore_IMF_Preedit_Attr *)calloc(1, sizeof(Ecore_IMF_Preedit_Attr));
|
|
if (!attr2) continue;
|
|
attr2->preedit_type = attr1->preedit_type;
|
|
attr2->start_index = attr1->start_index;
|
|
attr2->end_index = attr1->end_index;
|
|
|
|
*attrs = eina_list_append(*attrs, (void *)attr2);
|
|
}
|
|
}
|
|
else
|
|
*attrs = NULL;
|
|
}
|
|
}
|
|
|
|
void
|
|
ecore_imf_context_ibus_client_window_set(Ecore_IMF_Context *ctx, void *window)
|
|
{
|
|
EINA_LOG_DBG("canvas : %p", window);
|
|
IBusIMContext *ibusimcontext = (IBusIMContext *)ecore_imf_context_data_get(ctx);
|
|
EINA_SAFETY_ON_NULL_RETURN(ibusimcontext);
|
|
|
|
if (window != NULL)
|
|
ibusimcontext->client_window = (Ecore_X_Window)(Ecore_Window)window;
|
|
}
|
|
|
|
void
|
|
ecore_imf_context_ibus_client_canvas_set(Ecore_IMF_Context *ctx, void *canvas)
|
|
{
|
|
EINA_LOG_DBG("canvas : %p", canvas);
|
|
IBusIMContext *ibusimcontext = (IBusIMContext *)ecore_imf_context_data_get(ctx);
|
|
EINA_SAFETY_ON_NULL_RETURN(ibusimcontext);
|
|
|
|
if (canvas != NULL)
|
|
ibusimcontext->client_canvas = canvas;
|
|
}
|
|
|
|
static void
|
|
_ecore_imf_context_ibus_cursor_location_set(Ecore_IMF_Context *ctx)
|
|
{
|
|
IBusIMContext *ibusimcontext = (IBusIMContext *)ecore_imf_context_data_get(ctx);
|
|
Ecore_Evas *ee;
|
|
int canvas_x, canvas_y;
|
|
Ecore_X_Window client_window = 0;
|
|
|
|
EINA_SAFETY_ON_NULL_RETURN(ibusimcontext);
|
|
|
|
if (ibusimcontext->ibuscontext == NULL)
|
|
return;
|
|
|
|
if (ibusimcontext->client_window)
|
|
client_window = ibusimcontext->client_window;
|
|
else
|
|
{
|
|
if (ibusimcontext->client_canvas)
|
|
{
|
|
ee = ecore_evas_ecore_evas_get(ibusimcontext->client_canvas);
|
|
if (ee)
|
|
client_window = (Ecore_X_Window)ecore_evas_window_get(ee);
|
|
}
|
|
}
|
|
|
|
_ecore_imf_ibus_window_to_screen_geometry_get(client_window,
|
|
&canvas_x,
|
|
&canvas_y);
|
|
|
|
ibus_input_context_set_cursor_location(ibusimcontext->ibuscontext,
|
|
ibusimcontext->cursor_x + canvas_x,
|
|
ibusimcontext->cursor_y + canvas_y,
|
|
ibusimcontext->cursor_w,
|
|
ibusimcontext->cursor_h);
|
|
}
|
|
|
|
void
|
|
ecore_imf_context_ibus_cursor_location_set(Ecore_IMF_Context *ctx,
|
|
int x,
|
|
int y,
|
|
int w,
|
|
int h)
|
|
{
|
|
EINA_LOG_DBG("x : %d, y : %d, w, %d, h :%d", x, y, w, h);
|
|
IBusIMContext *ibusimcontext = (IBusIMContext *)ecore_imf_context_data_get(ctx);
|
|
EINA_SAFETY_ON_NULL_RETURN(ibusimcontext);
|
|
|
|
if (ibusimcontext->cursor_x != x ||
|
|
ibusimcontext->cursor_y != y ||
|
|
ibusimcontext->cursor_w != w ||
|
|
ibusimcontext->cursor_h != h)
|
|
{
|
|
ibusimcontext->cursor_x = x;
|
|
ibusimcontext->cursor_y = y;
|
|
ibusimcontext->cursor_w = w;
|
|
ibusimcontext->cursor_h = h;
|
|
|
|
_ecore_imf_context_ibus_cursor_location_set(ctx);
|
|
}
|
|
}
|
|
|
|
void
|
|
ecore_imf_context_ibus_use_preedit_set(Ecore_IMF_Context *ctx, Eina_Bool use_preedit)
|
|
{
|
|
EINA_LOG_DBG("preedit : %d", use_preedit);
|
|
IBusIMContext *ibusimcontext = (IBusIMContext *)ecore_imf_context_data_get(ctx);
|
|
EINA_SAFETY_ON_NULL_RETURN(ibusimcontext);
|
|
|
|
if (ibusimcontext->ibuscontext)
|
|
{
|
|
if (use_preedit)
|
|
ibusimcontext->caps |= IBUS_CAP_PREEDIT_TEXT;
|
|
else
|
|
ibusimcontext->caps &= ~IBUS_CAP_PREEDIT_TEXT;
|
|
|
|
ibus_input_context_set_capabilities(ibusimcontext->ibuscontext, ibusimcontext->caps);
|
|
}
|
|
}
|
|
|
|
static void
|
|
_ecore_imf_context_ibus_bus_connected_cb(IBusBus *bus EINA_UNUSED,
|
|
IBusIMContext *ibusimcontext)
|
|
{
|
|
EINA_LOG_DBG("ibus is connected");
|
|
|
|
if (ibusimcontext)
|
|
_ecore_imf_context_ibus_create(ibusimcontext);
|
|
}
|
|
|
|
static void
|
|
_ecore_imf_context_ibus_commit_text_cb(IBusInputContext *ibuscontext EINA_UNUSED,
|
|
IBusText *text,
|
|
IBusIMContext *ibusimcontext)
|
|
{
|
|
EINA_SAFETY_ON_NULL_RETURN(ibusimcontext);
|
|
EINA_SAFETY_ON_NULL_RETURN(text);
|
|
char *commit_str = text->text ? text->text : "";
|
|
|
|
EINA_LOG_DBG("commit string : %s", commit_str);
|
|
|
|
if (ibusimcontext->ctx)
|
|
{
|
|
ecore_imf_context_event_callback_call(ibusimcontext->ctx,
|
|
ECORE_IMF_CALLBACK_COMMIT,
|
|
(void *)commit_str);
|
|
|
|
_request_surrounding_text(ibusimcontext);
|
|
}
|
|
}
|
|
|
|
static XKeyEvent _ecore_imf_ibus_x_key_event_generate(Window win,
|
|
Eina_Bool press,
|
|
int keysym,
|
|
int keycode,
|
|
int modifiers)
|
|
{
|
|
XKeyEvent event;
|
|
Display *display = ecore_x_display_get();
|
|
|
|
event.display = display;
|
|
event.window = win;
|
|
event.root = display ? ecore_x_window_root_get(win) : 0;
|
|
event.subwindow = None;
|
|
event.time = 0;
|
|
event.x = 1;
|
|
event.y = 1;
|
|
event.x_root = 1;
|
|
event.y_root = 1;
|
|
event.same_screen = EINA_TRUE;
|
|
if (keycode == -1)
|
|
{
|
|
event.keycode = display ? XKeysymToKeycode(display, keysym) : 0;
|
|
event.state = 0;
|
|
}
|
|
else
|
|
{
|
|
event.keycode = keycode;
|
|
event.state = modifiers;
|
|
}
|
|
if (press)
|
|
event.type = KeyPress;
|
|
else
|
|
event.type = KeyRelease;
|
|
event.send_event = EINA_FALSE;
|
|
event.serial = 0;
|
|
|
|
return event;
|
|
}
|
|
|
|
static void
|
|
_ecore_imf_context_ibus_forward_key_event_cb(IBusInputContext *ibuscontext EINA_UNUSED,
|
|
guint keyval,
|
|
guint state,
|
|
IBusIMContext *ibusimcontext EINA_UNUSED)
|
|
{
|
|
EINA_LOG_DBG("keyval : %d, state : %d", keyval, state);
|
|
|
|
_ecore_imf_ibus_key_event_put(keyval, -1, state);
|
|
}
|
|
|
|
static void
|
|
_ecore_imf_context_ibus_delete_surrounding_text_cb(IBusInputContext *ibuscontext EINA_UNUSED,
|
|
gint offset_from_cursor,
|
|
guint nchars,
|
|
IBusIMContext *ibusimcontext)
|
|
{
|
|
EINA_SAFETY_ON_NULL_RETURN(ibusimcontext);
|
|
|
|
if (_focus_im_context != ibusimcontext->ctx)
|
|
return;
|
|
|
|
Ecore_IMF_Event_Delete_Surrounding ev;
|
|
ev.ctx = _focus_im_context;
|
|
ev.n_chars = nchars;
|
|
ev.offset = offset_from_cursor;
|
|
ecore_imf_context_event_callback_call(_focus_im_context,
|
|
ECORE_IMF_CALLBACK_DELETE_SURROUNDING,
|
|
&ev);
|
|
}
|
|
|
|
static void
|
|
_ecore_imf_context_ibus_update_preedit_text_cb(IBusInputContext *ibuscontext EINA_UNUSED,
|
|
IBusText *text,
|
|
gint cursor_pos,
|
|
gboolean visible,
|
|
IBusIMContext *ibusimcontext)
|
|
{
|
|
EINA_SAFETY_ON_NULL_RETURN(ibusimcontext);
|
|
EINA_SAFETY_ON_NULL_RETURN(text);
|
|
|
|
const char *str;
|
|
gboolean flag;
|
|
Ecore_IMF_Preedit_Attr *attr = NULL;
|
|
|
|
if (ibusimcontext->preedit_string)
|
|
free(ibusimcontext->preedit_string);
|
|
|
|
if (ibusimcontext->preedit_attrs)
|
|
{
|
|
EINA_LIST_FREE(ibusimcontext->preedit_attrs, attr)
|
|
free(attr);
|
|
|
|
ibusimcontext->preedit_attrs = NULL;
|
|
}
|
|
|
|
str = text->text;
|
|
|
|
if (str)
|
|
ibusimcontext->preedit_string = strdup(str);
|
|
else
|
|
ibusimcontext->preedit_string = strdup("");
|
|
|
|
if (text->attrs)
|
|
{
|
|
unsigned int i;
|
|
unsigned int pos;
|
|
unsigned int preedit_length;
|
|
preedit_length = strlen(ibusimcontext->preedit_string);
|
|
Eina_Bool *attrs_flag = calloc(1, sizeof(Eina_Bool)*preedit_length);
|
|
|
|
for (i = 0; ; i++)
|
|
{
|
|
attr = NULL;
|
|
IBusAttribute *ibus_attr = ibus_attr_list_get(text->attrs, i);
|
|
if (ibus_attr == NULL)
|
|
break;
|
|
|
|
attr = (Ecore_IMF_Preedit_Attr *)calloc(1, sizeof(Ecore_IMF_Preedit_Attr));
|
|
if (attr == NULL)
|
|
continue;
|
|
|
|
attr->start_index = utf8_offset_to_index(ibusimcontext->preedit_string,
|
|
ibus_attr->start_index);
|
|
attr->end_index = utf8_offset_to_index(ibusimcontext->preedit_string,
|
|
ibus_attr->end_index);
|
|
|
|
switch (ibus_attr->type)
|
|
{
|
|
case IBUS_ATTR_TYPE_FOREGROUND:
|
|
attr->preedit_type = ECORE_IMF_PREEDIT_TYPE_SUB2;
|
|
for (pos = attr->start_index; pos < attr->end_index; ++pos)
|
|
attrs_flag[pos] = 1;
|
|
break;
|
|
default:
|
|
if (attr)
|
|
{
|
|
free(attr);
|
|
attr = NULL;
|
|
}
|
|
continue;
|
|
}
|
|
|
|
if (attr)
|
|
ibusimcontext->preedit_attrs = eina_list_append(ibusimcontext->preedit_attrs,
|
|
(void *)attr);
|
|
}
|
|
|
|
// Add underline for all characters which don't have attribute.
|
|
for (pos = 0; pos < preedit_length; ++pos)
|
|
{
|
|
if (!attrs_flag[pos])
|
|
{
|
|
int begin_pos = pos;
|
|
|
|
while (pos < preedit_length && !attrs_flag[pos])
|
|
++pos;
|
|
|
|
attr = (Ecore_IMF_Preedit_Attr *)calloc(1, sizeof(Ecore_IMF_Preedit_Attr));
|
|
if (attr == NULL)
|
|
continue;
|
|
attr->preedit_type = ECORE_IMF_PREEDIT_TYPE_SUB1;
|
|
attr->start_index = begin_pos;
|
|
attr->end_index = pos;
|
|
ibusimcontext->preedit_attrs = eina_list_append(ibusimcontext->preedit_attrs,
|
|
(void *)attr);
|
|
}
|
|
}
|
|
|
|
if (attrs_flag)
|
|
free(attrs_flag);
|
|
|
|
ibusimcontext->preedit_attrs = eina_list_sort(ibusimcontext->preedit_attrs,
|
|
eina_list_count(ibusimcontext->preedit_attrs),
|
|
sort_cb);
|
|
}
|
|
|
|
ibusimcontext->preedit_cursor_pos = cursor_pos;
|
|
|
|
EINA_LOG_DBG("string : %s, cursor : %d",
|
|
ibusimcontext->preedit_string,
|
|
ibusimcontext->preedit_cursor_pos);
|
|
|
|
flag = ibusimcontext->preedit_visible != visible;
|
|
ibusimcontext->preedit_visible = visible;
|
|
|
|
if (ibusimcontext->preedit_visible)
|
|
{
|
|
if (flag)
|
|
{
|
|
ecore_imf_context_event_callback_call(ibusimcontext->ctx,
|
|
ECORE_IMF_CALLBACK_PREEDIT_START,
|
|
NULL);
|
|
}
|
|
|
|
ecore_imf_context_event_callback_call(ibusimcontext->ctx,
|
|
ECORE_IMF_CALLBACK_PREEDIT_CHANGED,
|
|
NULL);
|
|
}
|
|
else
|
|
{
|
|
if (flag)
|
|
{
|
|
ecore_imf_context_event_callback_call(ibusimcontext->ctx,
|
|
ECORE_IMF_CALLBACK_PREEDIT_CHANGED,
|
|
NULL);
|
|
}
|
|
|
|
ecore_imf_context_event_callback_call(ibusimcontext->ctx,
|
|
ECORE_IMF_CALLBACK_PREEDIT_END,
|
|
NULL);
|
|
}
|
|
}
|
|
|
|
static void
|
|
_ecore_imf_context_ibus_show_preedit_text_cb(IBusInputContext *ibuscontext EINA_UNUSED,
|
|
IBusIMContext *ibusimcontext)
|
|
{
|
|
EINA_LOG_DBG("preedit visible : %d", ibusimcontext->preedit_visible);
|
|
EINA_SAFETY_ON_NULL_RETURN(ibusimcontext);
|
|
|
|
if (ibusimcontext->preedit_visible == EINA_TRUE)
|
|
return;
|
|
|
|
ibusimcontext->preedit_visible = EINA_TRUE;
|
|
|
|
// call preedit start
|
|
ecore_imf_context_event_callback_call(ibusimcontext->ctx,
|
|
ECORE_IMF_CALLBACK_PREEDIT_START,
|
|
NULL);
|
|
|
|
// call preedit changed
|
|
ecore_imf_context_event_callback_call(ibusimcontext->ctx,
|
|
ECORE_IMF_CALLBACK_PREEDIT_CHANGED,
|
|
NULL);
|
|
|
|
_request_surrounding_text(ibusimcontext);
|
|
}
|
|
|
|
static void
|
|
_ecore_imf_context_ibus_hide_preedit_text_cb(IBusInputContext *ibuscontext EINA_UNUSED,
|
|
IBusIMContext *ibusimcontext)
|
|
{
|
|
EINA_LOG_DBG("%s", __func__);
|
|
EINA_SAFETY_ON_NULL_RETURN(ibusimcontext);
|
|
|
|
if (ibusimcontext->preedit_visible == EINA_FALSE)
|
|
return;
|
|
|
|
ibusimcontext->preedit_visible = EINA_FALSE;
|
|
|
|
// call preedit changed
|
|
ecore_imf_context_event_callback_call(ibusimcontext->ctx,
|
|
ECORE_IMF_CALLBACK_PREEDIT_CHANGED,
|
|
NULL);
|
|
|
|
// call preedit end
|
|
ecore_imf_context_event_callback_call(ibusimcontext->ctx,
|
|
ECORE_IMF_CALLBACK_PREEDIT_END,
|
|
NULL);
|
|
}
|
|
|
|
static void
|
|
_ecore_imf_context_ibus_destroy_cb(IBusInputContext *ibuscontext EINA_UNUSED,
|
|
IBusIMContext *ibusimcontext)
|
|
{
|
|
EINA_LOG_DBG("%s", __func__);
|
|
EINA_SAFETY_ON_NULL_RETURN(ibusimcontext);
|
|
|
|
g_object_unref(ibusimcontext->ibuscontext);
|
|
ibusimcontext->ibuscontext = NULL;
|
|
|
|
/* clear preedit */
|
|
ibusimcontext->preedit_visible = EINA_FALSE;
|
|
ibusimcontext->preedit_cursor_pos = 0;
|
|
free(ibusimcontext->preedit_string);
|
|
ibusimcontext->preedit_string = NULL;
|
|
|
|
// call preedit changed
|
|
ecore_imf_context_event_callback_call(ibusimcontext->ctx,
|
|
ECORE_IMF_CALLBACK_PREEDIT_CHANGED,
|
|
NULL);
|
|
|
|
// call preedit end
|
|
ecore_imf_context_event_callback_call(ibusimcontext->ctx,
|
|
ECORE_IMF_CALLBACK_PREEDIT_END,
|
|
NULL);
|
|
}
|
|
|
|
static void
|
|
_ecore_imf_context_ibus_create(IBusIMContext *ibusimcontext)
|
|
{
|
|
EINA_LOG_DBG("%s", __func__);
|
|
EINA_SAFETY_ON_NULL_RETURN(ibusimcontext);
|
|
|
|
ibusimcontext->ibuscontext = ibus_bus_create_input_context(_bus, "ecore");
|
|
|
|
g_return_if_fail(ibusimcontext->ibuscontext != NULL);
|
|
|
|
g_signal_connect(ibusimcontext->ibuscontext,
|
|
"commit-text",
|
|
G_CALLBACK (_ecore_imf_context_ibus_commit_text_cb),
|
|
ibusimcontext);
|
|
g_signal_connect(ibusimcontext->ibuscontext,
|
|
"forward-key-event",
|
|
G_CALLBACK (_ecore_imf_context_ibus_forward_key_event_cb),
|
|
ibusimcontext);
|
|
g_signal_connect(ibusimcontext->ibuscontext,
|
|
"delete-surrounding-text",
|
|
G_CALLBACK (_ecore_imf_context_ibus_delete_surrounding_text_cb),
|
|
ibusimcontext);
|
|
g_signal_connect(ibusimcontext->ibuscontext,
|
|
"update-preedit-text",
|
|
G_CALLBACK (_ecore_imf_context_ibus_update_preedit_text_cb),
|
|
ibusimcontext);
|
|
g_signal_connect(ibusimcontext->ibuscontext,
|
|
"show-preedit-text",
|
|
G_CALLBACK (_ecore_imf_context_ibus_show_preedit_text_cb),
|
|
ibusimcontext);
|
|
g_signal_connect(ibusimcontext->ibuscontext,
|
|
"hide-preedit-text",
|
|
G_CALLBACK (_ecore_imf_context_ibus_hide_preedit_text_cb),
|
|
ibusimcontext);
|
|
g_signal_connect(ibusimcontext->ibuscontext, "destroy",
|
|
G_CALLBACK (_ecore_imf_context_ibus_destroy_cb),
|
|
ibusimcontext);
|
|
|
|
ibus_input_context_set_capabilities(ibusimcontext->ibuscontext,
|
|
ibusimcontext->caps);
|
|
|
|
if (ibusimcontext->has_focus)
|
|
ibus_input_context_focus_in(ibusimcontext->ibuscontext);
|
|
}
|