efl/src/modules/ecore_imf/ibus/ibus_imcontext.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);
}