diff --git a/legacy/ecore/src/modules/immodules/xim/Makefile.am b/legacy/ecore/src/modules/immodules/xim/Makefile.am new file mode 100644 index 0000000000..d84cab28e3 --- /dev/null +++ b/legacy/ecore/src/modules/immodules/xim/Makefile.am @@ -0,0 +1,25 @@ +MAINTAINERCLEANFILES = Makefile.in + +AM_CPPFLAGS = \ +-I$(top_srcdir) \ +-I$(top_srcdir)/src/lib/ecore \ +-I$(top_srcdir)/src/lib/ecore_input \ +-I$(top_srcdir)/src/lib/ecore_x \ +-I$(top_srcdir)/src/lib/ecore_imf \ +-I$(top_builddir)/src/lib/ecore \ +-I$(top_builddir)/src/lib/ecore_input \ +-I$(top_builddir)/src/lib/ecore_x \ +-I$(top_builddir)/src/lib/ecore_imf \ +-DPACKAGE_LIB_DIR=\"$(libdir)\" \ +-DPACKAGE_DATA_DIR=\"$(datadir)/$(PACKAGE)\" \ +@EINA_CFLAGS@ + +pkgdir = $(libdir)/ecore/immodules + +pkg_LTLIBRARIES = xim.la +xim_la_SOURCES = \ +ecore_imf_xim.c +xim_la_LIBADD = $(top_builddir)/src/lib/ecore_imf/libecore_imf.la +xim_la_LDFLAGS = -no-undefined @lt_enable_auto_import@ -module -avoid-version +xim_la_LIBTOOLFLAGS = --tag=disable-static +xim_la_DEPENDENCIES = $(top_builddir)/config.h diff --git a/legacy/ecore/src/modules/immodules/xim/ecore_imf_xim.c b/legacy/ecore/src/modules/immodules/xim/ecore_imf_xim.c new file mode 100644 index 0000000000..5b3b060df8 --- /dev/null +++ b/legacy/ecore/src/modules/immodules/xim/ecore_imf_xim.c @@ -0,0 +1,1150 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef HAVE_CONFIG_H +# include +#endif + +#define CLAMP(x, low, high) (x > high) ? high : (x < low) ? low : x +#define _(x) x + +#ifdef ENABLE_XIM +static Eina_List *open_ims = NULL; +#endif + +typedef struct _XIM_Im_Info XIM_Im_Info; +struct _XIM_Im_Info +{ + Ecore_X_Window win; + char *locale; + XIM im; + Eina_List *ics; + Eina_Bool reconnecting; + XIMStyles *xim_styles; + // Eina_Bool supports_string_conversion; +}; + +typedef struct _Ecore_IMF_Context_Data Ecore_IMF_Context_Data; +struct _Ecore_IMF_Context_Data +{ + Ecore_X_Window win; + long mask; + XIC ic; /* Input context for composed characters */ + char *locale; + XIM_Im_Info *im_info; + int preedit_length; + int preedit_size; + int preedit_cursor; + Eina_Unicode *preedit_chars; + Eina_Bool use_preedit; + Eina_Bool finalizing; + Eina_Bool has_focus; + Eina_Bool in_toplevel; + + XIMCallback preedit_start_cb; + XIMCallback preedit_done_cb; + XIMCallback preedit_draw_cb; + XIMCallback preedit_caret_cb; +}; + +/* prototype */ +Ecore_IMF_Context_Data *imf_context_data_new(); +void imf_context_data_destroy(Ecore_IMF_Context_Data *imf_context_data); + +#ifdef ENABLE_XIM +static void reinitialize_ic(Ecore_IMF_Context *ctx); +static void reinitialize_all_ics(XIM_Im_Info *info); +static void set_ic_client_window(Ecore_IMF_Context *ctx, + Ecore_X_Window window); +static int preedit_start_callback(XIC xic, + XPointer client_data, + XPointer call_data); +static void preedit_done_callback(XIC xic, + XPointer client_data, + XPointer call_data); +static int xim_text_to_utf8(Ecore_IMF_Context *ctx, + XIMText *xim_text, + char **text); +static void preedit_draw_callback(XIC xic, + XPointer client_data, + XIMPreeditDrawCallbackStruct *call_data); +static void preedit_caret_callback(XIC xic, + XPointer client_data, + XIMPreeditCaretCallbackStruct *call_data); +static XVaNestedList preedit_callback_set(Ecore_IMF_Context *ctx); +static XIC get_ic(Ecore_IMF_Context *ctx); +static XIM_Im_Info *get_im(Ecore_X_Window window, + char *locale); +static void xim_info_try_im(XIM_Im_Info *info); +static void xim_info_display_closed(Ecore_X_Display *display, + int is_error, + XIM_Im_Info *info); +static void xim_instantiate_callback(Display *display, + XPointer client_data, + XPointer call_data); +static void setup_im(XIM_Im_Info *info); +static void xim_destroy_callback(XIM xim, + XPointer client_data, + XPointer call_data); +#endif + +static void +_ecore_imf_context_xim_add(Ecore_IMF_Context *ctx) +{ + EINA_LOG_DBG("in"); +#ifdef ENABLE_XIM + Ecore_IMF_Context_Data *imf_context_data = NULL; + + imf_context_data = imf_context_data_new(); + if(!imf_context_data) return; + + imf_context_data->use_preedit = EINA_TRUE; + imf_context_data->finalizing = EINA_FALSE; + imf_context_data->has_focus = EINA_FALSE; + imf_context_data->in_toplevel = EINA_FALSE; + + ecore_imf_context_data_set(ctx, imf_context_data); +#endif +} + +static void +_ecore_imf_context_xim_del(Ecore_IMF_Context *ctx) +{ + EINA_LOG_DBG("in"); +#ifdef ENABLE_XIM + Ecore_IMF_Context_Data *imf_context_data; + imf_context_data = ecore_imf_context_data_get(ctx); + + imf_context_data->finalizing = EINA_TRUE; + if(imf_context_data->im_info && !imf_context_data->im_info->ics->next) + { + if(imf_context_data->im_info->reconnecting == EINA_TRUE) + { + Ecore_X_Display *dsp; + dsp = ecore_x_display_get(); + XUnregisterIMInstantiateCallback (dsp, + NULL, NULL, NULL, + xim_instantiate_callback, + (XPointer)imf_context_data->im_info); + } + else if(imf_context_data->im_info->im) + { + XIMCallback im_destroy_callback; + im_destroy_callback.client_data = NULL; + im_destroy_callback.callback = NULL; + XSetIMValues (imf_context_data->im_info->im, + XNDestroyCallback, &im_destroy_callback, + NULL); + } + } + + set_ic_client_window(ctx, 0); + + imf_context_data_destroy(imf_context_data); +#endif +} + +static void +_ecore_imf_context_xim_client_window_set(Ecore_IMF_Context *ctx, + void *window) +{ + EINA_LOG_DBG("in"); +#ifdef ENABLE_XIM + set_ic_client_window(ctx, (Ecore_X_Window)((Ecore_Window)window)); +#endif +} + +static void +_ecore_imf_context_xim_preedit_string_get(Ecore_IMF_Context *ctx, + char **str, + int *cursor_pos) +{ + EINA_LOG_DBG("in"); +#ifdef ENABLE_XIM + Ecore_IMF_Context_Data *imf_context_data; + char *utf8; + int len; + imf_context_data = ecore_imf_context_data_get(ctx); + utf8 = eina_unicode_unicode_to_utf8(imf_context_data->preedit_chars, + &len); + if(str) + *str = utf8; + else + free(utf8); + + if(cursor_pos) + *cursor_pos = imf_context_data->preedit_cursor; +#else + if(str) + *str = NULL; + if(cursor_pos) + *cursor_pos = 0; +#endif +} + +static void +_ecore_imf_context_xim_focus_in(Ecore_IMF_Context *ctx) +{ + EINA_LOG_DBG("in"); +#ifdef ENABLE_XIM + XIC ic; + Ecore_IMF_Context_Data *imf_context_data; + imf_context_data = ecore_imf_context_data_get(ctx); + ic = imf_context_data->ic; + imf_context_data->has_focus = EINA_TRUE; + if(ic) + { + char *str; + +#ifdef X_HAVE_UTF8_STRING + if ((str = Xutf8ResetIC(ic))) +#else + if ((str = XmbResetIC(ic))) +#endif + XFree(str); + + XSetICFocus(ic); + } +#endif +} + +static void +_ecore_imf_context_xim_focus_out(Ecore_IMF_Context *ctx) { + EINA_LOG_DBG("%s in", __FUNCTION__); +#ifdef ENABLE_XIM + XIC ic; + Ecore_IMF_Context_Data *imf_context_data; + imf_context_data = ecore_imf_context_data_get(ctx); + if(imf_context_data->has_focus == EINA_TRUE) + { + imf_context_data->has_focus = EINA_FALSE; + ic = imf_context_data->ic; + if(ic) + XUnsetICFocus(ic); + } +#endif +} + +static void +_ecore_imf_context_xim_reset(Ecore_IMF_Context *ctx) { + EINA_LOG_DBG("%s in", __FUNCTION__); +#ifdef ENABLE_XIM + XIC ic; + Ecore_IMF_Context_Data *imf_context_data; + char *result; + + /* restore conversion state after resetting ic later */ + XIMPreeditState preedit_state = XIMPreeditUnKnown; + XVaNestedList preedit_attr; + Eina_Bool have_preedit_state = EINA_FALSE; + + imf_context_data = ecore_imf_context_data_get(ctx); + ic = imf_context_data->ic; + if(!ic) + return; + + if(imf_context_data->preedit_length == 0) + return; + + preedit_attr = XVaCreateNestedList(0, + XNPreeditState, &preedit_state, + NULL); + if(!XGetICValues(ic, + XNPreeditAttributes, preedit_attr, + NULL)) + have_preedit_state = EINA_TRUE; + + XFree(preedit_attr); + + result = XmbResetIC(ic); + + preedit_attr = XVaCreateNestedList(0, + XNPreeditState, preedit_state, + NULL); + if(have_preedit_state) + XSetICValues(ic, + XNPreeditAttributes, preedit_attr, + NULL); + + XFree(preedit_attr); + + if(result) + { + char *result_utf8 = strdup(result); + if(result_utf8) + { + ecore_imf_context_commit_event_add(ctx, result_utf8); + free(result_utf8); + } + } + + if(imf_context_data->preedit_length) + { + imf_context_data->preedit_length = 0; + ecore_imf_context_preedit_changed_event_add(ctx); + } + + XFree (result); +#endif +} + +static void +_ecore_imf_context_xim_use_preedit_set(Ecore_IMF_Context *ctx, + Eina_Bool use_preedit) +{ + EINA_LOG_DBG("in"); +#ifdef ENABLE_XIM + Ecore_IMF_Context_Data *imf_context_data; + imf_context_data = ecore_imf_context_data_get(ctx); + + use_preedit = use_preedit != EINA_FALSE; + + if(imf_context_data->use_preedit != use_preedit) + { + imf_context_data->use_preedit = use_preedit; + reinitialize_ic(ctx); + } +#endif +} + +#ifdef ENABLE_XIM +static unsigned int +_ecore_x_event_reverse_modifiers(unsigned int state) +{ + unsigned int modifiers = 0; + + /**< "Control" is pressed */ + if(state & ECORE_IMF_KEYBOARD_MODIFIER_CTRL) + modifiers |= ControlMask; + + /**< "Alt" is pressed */ + if(state & ECORE_IMF_KEYBOARD_MODIFIER_ALT) + modifiers |= Mod1Mask; + + /**< "Shift" is pressed */ + if(state & ECORE_IMF_KEYBOARD_MODIFIER_SHIFT) + modifiers |= ShiftMask; + + /**< "Win" (between "Ctrl" and "A */ + if(state & ECORE_IMF_KEYBOARD_MODIFIER_WIN) + modifiers |= Mod5Mask; + + return modifiers; +} + +static unsigned int +_ecore_x_event_reverse_locks(unsigned int state) { + unsigned int locks = 0; + + /**< "Num" lock is active */ + if(state & ECORE_IMF_KEYBOARD_LOCK_NUM) + locks |= Mod3Mask; + + if(state & ECORE_IMF_KEYBOARD_LOCK_CAPS) + locks |= LockMask; + + if(state & ECORE_IMF_KEYBOARD_LOCK_SCROLL) + ; /* XXX */ + + return locks; +} + +static KeyCode +_keycode_get(Ecore_X_Display *dsp, + const char *keyname) { + KeyCode keycode; + + // EINA_LOG_DBG("keyname:%s keysym:%lu", keyname, XStringToKeysym(keyname)); + if(strcmp(keyname, "Keycode-0") == 0) + { + keycode = 0; + } + else { + keycode = XKeysymToKeycode(dsp, XStringToKeysym(keyname)); + } + + return keycode; +} + +#endif + +static Eina_Bool +_ecore_imf_context_xim_filter_event(Ecore_IMF_Context *ctx, + Ecore_IMF_Event_Type type, + Ecore_IMF_Event *event) { + EINA_LOG_DBG("%s in", __FUNCTION__); +#ifdef ENABLE_XIM + Ecore_IMF_Context_Data *imf_context_data; + XIC ic; + + Ecore_X_Display *dsp; + Ecore_X_Window win; + + int val; + char compose_buffer[256]; + KeySym sym; + char *compose = NULL; + char *tmp = NULL; + Eina_Bool result = EINA_FALSE; + + imf_context_data = ecore_imf_context_data_get(ctx); + ic = imf_context_data->ic; + if(!ic) + { + ic = get_ic(ctx); + } + + if(type == ECORE_IMF_EVENT_KEY_DOWN) + { + XKeyPressedEvent xev; + Ecore_IMF_Event_Key_Down *ev = (Ecore_IMF_Event_Key_Down *)event; + EINA_LOG_DBG("ECORE_IMF_EVENT_KEY_DOWN"); + + dsp = ecore_x_display_get(); + win = imf_context_data->win; + + xev.type = KeyPress; + xev.serial = 0; /* hope it doesn't matter */ + xev.send_event = 0; + xev.display = dsp; + xev.window = win; + xev.root = ecore_x_window_root_get(win); + xev.subwindow = win; + xev.time = ev->timestamp; + xev.x = xev.x_root = 0; + xev.y = xev.y_root = 0; + xev.state = 0; + xev.state |= _ecore_x_event_reverse_modifiers(ev->modifiers); + xev.state |= _ecore_x_event_reverse_locks(ev->locks); + xev.keycode = _keycode_get(dsp, ev->keyname); + xev.same_screen = True; + + if(ic) + { + Status mbstatus; +#ifdef X_HAVE_UTF8_STRING + val = Xutf8LookupString(ic, + &xev, + compose_buffer, + sizeof(compose_buffer) - 1, + &sym, + &mbstatus); +#else /* ifdef X_HAVE_UTF8_STRING */ + val = XmbLookupString(ic, + &xev, + compose_buffer, + sizeof(compose_buffer) - 1, + &sym, + &mbstatus); +#endif /* ifdef X_HAVE_UTF8_STRING */ + if (mbstatus == XBufferOverflow) + { + tmp = malloc(sizeof (char) * (val + 1)); + if (!tmp) + { + return EINA_FALSE; + } + + compose = tmp; + +#ifdef X_HAVE_UTF8_STRING + val = Xutf8LookupString(ic, + (XKeyEvent *)&xev, + tmp, + val, + &sym, + &mbstatus); +#else /* ifdef X_HAVE_UTF8_STRING */ + val = XmbLookupString(ic, + (XKeyEvent *)&xev, + tmp, + val, + &sym, + &mbstatus); +#endif /* ifdef X_HAVE_UTF8_STRING */ + if (val > 0) + { + tmp[val] = '\0'; +#ifndef X_HAVE_UTF8_STRING + compose = eina_str_convert(nl_langinfo(CODESET), + "UTF-8", tmp); + free(tmp); + tmp = compose; +#endif /* ifndef X_HAVE_UTF8_STRING */ + } + else + compose = NULL; + } + else if (val > 0) + { + compose_buffer[val] = '\0'; +#ifdef X_HAVE_UTF8_STRING + compose = strdup(compose_buffer); +#else /* ifdef X_HAVE_UTF8_STRING */ + compose = eina_str_convert(nl_langinfo(CODESET), "UTF-8", + compose_buffer); +#endif /* ifdef X_HAVE_UTF8_STRING */ + } + } + else { + XComposeStatus status; + val = XLookupString(&xev, + compose_buffer, + sizeof(compose_buffer), + &sym, + &status); + if (val > 0) + { + compose_buffer[val] = '\0'; + compose = eina_str_convert(nl_langinfo(CODESET), + "UTF-8", compose_buffer); + } + } + + if(compose) + { + Eina_Unicode *unicode; + int len; + unicode = eina_unicode_utf8_to_unicode(compose, &len); + if(!unicode) abort(); + if(unicode[0] >= 0x20 && unicode[0] != 0x7f) + { + ecore_imf_context_commit_event_add(ctx, compose); + result = EINA_TRUE; + } + free(compose); + free(unicode); + } + } + + return result; +#else + return EINA_FALSE; +#endif +} + +static const Ecore_IMF_Context_Info xim_info = { + .id = "xim", + .description = _("X input method"), + .default_locales = "ko:ja:th:zh", + .canvas_type = "evas", + .canvas_required = 1, +}; + +static Ecore_IMF_Context_Class xim_class = { + .add = _ecore_imf_context_xim_add, + .del = _ecore_imf_context_xim_del, + .client_window_set = _ecore_imf_context_xim_client_window_set, + .client_canvas_set = NULL, + .show = NULL, + .hide = NULL, + .preedit_string_get = _ecore_imf_context_xim_preedit_string_get, + .focus_in = _ecore_imf_context_xim_focus_in, + .focus_out = _ecore_imf_context_xim_focus_out, + .reset = _ecore_imf_context_xim_reset, + .cursor_position_set = NULL, + .use_preedit_set = _ecore_imf_context_xim_use_preedit_set, + .input_mode_set = NULL, + .filter_event = _ecore_imf_context_xim_filter_event, + .preedit_string_with_attributes_get = NULL, + .prediction_allow_set = NULL, + .autocapital_type_set = NULL, + .control_panel_show = NULL, + .control_panel_hide = NULL, + .input_panel_layout_set = NULL, + .input_panel_layout_get = NULL, + .input_panel_language_set = NULL, + .input_panel_language_get = NULL, + .cursor_location_set = NULL, +}; + +static Ecore_IMF_Context * +xim_imf_module_create(void) { + EINA_LOG_DBG("%s in", __FUNCTION__); + Ecore_IMF_Context *ctx = NULL; + + ctx = ecore_imf_context_new(&xim_class); + if(!ctx) + goto error; + + return ctx; + +error: + free(ctx); + return NULL; +} + +static Ecore_IMF_Context * +xim_imf_module_exit(void) { + return NULL; +} + +Eina_Bool +ecore_imf_xim_init(void) { + EINA_LOG_DBG("%s in", __FUNCTION__); + eina_init(); + ecore_x_init(NULL); + ecore_imf_module_register(&xim_info, + xim_imf_module_create, + xim_imf_module_exit); + + return EINA_TRUE; +} + +void +ecore_imf_xim_shutdown(void) { + while (open_ims) { + XIM_Im_Info *info = open_ims->data; + Ecore_X_Display *display = ecore_x_display_get(); + + xim_info_display_closed(display, EINA_FALSE, info); + } + + ecore_x_shutdown(); + eina_shutdown(); +} + +EINA_MODULE_INIT(ecore_imf_xim_init); +EINA_MODULE_SHUTDOWN(ecore_imf_xim_shutdown); + +#ifdef ENABLE_XIM +/* + * internal functions + */ +Ecore_IMF_Context_Data * +imf_context_data_new() +{ + Ecore_IMF_Context_Data *imf_context_data = NULL; + char *locale; + + locale = setlocale(LC_CTYPE, ""); + if(!locale) return NULL; + + if(!XSupportsLocale()) return NULL; + + imf_context_data = calloc(1, sizeof(Ecore_IMF_Context_Data)); + if(!imf_context_data) return NULL; + + imf_context_data->locale = strdup(locale); + if(!imf_context_data->locale) goto error; + + return imf_context_data; +error: + imf_context_data_destroy(imf_context_data); + return NULL; +} + +void +imf_context_data_destroy(Ecore_IMF_Context_Data *imf_context_data) { + if(!imf_context_data) + return; + + if(imf_context_data->ic) + XDestroyIC(imf_context_data->ic); + + free(imf_context_data->locale); + free(imf_context_data); +} + +static int +preedit_start_callback(XIC xic, + XPointer client_data, + XPointer call_data) +{ + EINA_LOG_DBG("in"); + Ecore_IMF_Context *ctx = (Ecore_IMF_Context *)client_data; + Ecore_IMF_Context_Data *imf_context_data; + imf_context_data = ecore_imf_context_data_get(ctx); + + if(imf_context_data->finalizing == EINA_FALSE) + ecore_imf_context_preedit_start_event_add(ctx); + + return -1; +} + +static void +preedit_done_callback(XIC xic, + XPointer client_data, + XPointer call_data) +{ + EINA_LOG_DBG("in"); + Ecore_IMF_Context *ctx = (Ecore_IMF_Context *)client_data; + Ecore_IMF_Context_Data *imf_context_data; + imf_context_data = ecore_imf_context_data_get(ctx); + + if(imf_context_data->preedit_length) + { + imf_context_data->preedit_length = 0; + ecore_imf_context_preedit_changed_event_add(ctx); + } + + if(imf_context_data->finalizing == EINA_FALSE) + ecore_imf_context_preedit_end_event_add(ctx); +} + +/* FIXME */ +static int +xim_text_to_utf8(Ecore_IMF_Context *ctx, + XIMText *xim_text, + char **text) +{ + int text_length = 0; + char *result = NULL; + + if(xim_text && xim_text->string.multi_byte) + { + if(xim_text->encoding_is_wchar) + { + EINA_LOG_WARN("Wide character return from Xlib not currently supported"); + *text = NULL; + return 0; + } + + /* XXX Convert to UTF-8 */ + result = strdup(xim_text->string.multi_byte); + if(result) + { + text_length = eina_unicode_utf8_get_len(result); + if (text_length != xim_text->length) + { + EINA_LOG_WARN("Size mismatch when converting text from input method: supplied length = %d\n, result length = %d", xim_text->length, text_length); + } + } + else { + EINA_LOG_WARN("Error converting text from IM to UCS-4"); + *text = NULL; + return 0; + } + + *text = result; + return text_length; + } + else { + *text = NULL; + return 0; + } +} + +static void +preedit_draw_callback(XIC xic, + XPointer client_data, + XIMPreeditDrawCallbackStruct *call_data) +{ + EINA_LOG_DBG("in"); + Ecore_IMF_Context *ctx = (Ecore_IMF_Context *)client_data; + Ecore_IMF_Context_Data *imf_context_data = ecore_imf_context_data_get(ctx); + XIMText *t = call_data->text; + char *tmp; + Eina_Unicode *new_text = NULL; + int new_length; + int new_text_length; + int diff; + int chg_first; + int chg_length; + int i; + + /* XXX */ + chg_first = CLAMP(call_data->chg_first, 0, imf_context_data->preedit_length); + chg_length = CLAMP(call_data->chg_length, 0, + imf_context_data->preedit_length - chg_first); + + new_text_length = xim_text_to_utf8(ctx, t, &tmp); + if(tmp) + { + int tmp_len; + new_text = eina_unicode_utf8_to_unicode((const char *)tmp, &tmp_len); + free(tmp); + } + + diff = new_text_length - chg_length; + new_length = imf_context_data->preedit_length + diff; + if(new_length > imf_context_data->preedit_size) + { + Eina_Unicode *tmp_chars = NULL; + imf_context_data->preedit_size = new_length; + + if(imf_context_data->preedit_chars) + { + tmp_chars = eina_unicode_strdup(imf_context_data->preedit_chars); + free(imf_context_data->preedit_chars); + imf_context_data->preedit_chars = calloc(new_length + 1, + sizeof(Eina_Unicode)); + eina_unicode_strcpy(imf_context_data->preedit_chars, tmp_chars); + free(tmp_chars); + } + else { + imf_context_data->preedit_chars = calloc(new_length + 1, + sizeof(Eina_Unicode)); + } + // XXX feedback? + } + + if(diff < 0) + { + for(i = chg_first + chg_length; i < imf_context_data->preedit_length; i++) { + imf_context_data->preedit_chars[i + diff] = + imf_context_data->preedit_chars[i]; + } + } + else { + for(i = imf_context_data->preedit_length - 1; i >= chg_first + chg_length; i--) { + imf_context_data->preedit_chars[i + diff] = + imf_context_data->preedit_chars[i]; + } + } + + for(i = 0; i < new_text_length; i++) { + imf_context_data->preedit_chars[chg_first + i] = new_text[i]; + } + + imf_context_data->preedit_length += diff; + free(new_text); + + if(imf_context_data->finalizing == EINA_FALSE) + ecore_imf_context_preedit_changed_event_add(ctx); +} + +static void +preedit_caret_callback(XIC xic, + XPointer client_data, + XIMPreeditCaretCallbackStruct *call_data) +{ + EINA_LOG_DBG("in"); + Ecore_IMF_Context *ctx = (Ecore_IMF_Context *)client_data; + Ecore_IMF_Context_Data *imf_context_data; + imf_context_data = ecore_imf_context_data_get(ctx); + + if(call_data->direction == XIMAbsolutePosition) + { + // printf("call_data->position:%d\n", call_data->position); + imf_context_data->preedit_cursor = call_data->position; + if(imf_context_data->finalizing == EINA_FALSE) + ecore_imf_context_preedit_changed_event_add(ctx); + } +} + +static XVaNestedList +preedit_callback_set(Ecore_IMF_Context *ctx) +{ + Ecore_IMF_Context_Data *imf_context_data; + imf_context_data = ecore_imf_context_data_get(ctx); + + imf_context_data->preedit_start_cb.client_data = (XPointer)ctx; + imf_context_data->preedit_start_cb.callback = (XIMProc)preedit_start_callback; + + imf_context_data->preedit_done_cb.client_data = (XPointer)ctx; + imf_context_data->preedit_done_cb.callback = (XIMProc)preedit_done_callback; + + imf_context_data->preedit_draw_cb.client_data = (XPointer)ctx; + imf_context_data->preedit_draw_cb.callback = (XIMProc)preedit_draw_callback; + + imf_context_data->preedit_caret_cb.client_data = (XPointer)ctx; + imf_context_data->preedit_caret_cb.callback = (XIMProc)preedit_caret_callback; + + return XVaCreateNestedList(0, + XNPreeditStartCallback, + &imf_context_data->preedit_start_cb, + XNPreeditDoneCallback, + &imf_context_data->preedit_done_cb, + XNPreeditDrawCallback, + &imf_context_data->preedit_draw_cb, + XNPreeditCaretCallback, + &imf_context_data->preedit_caret_cb, + NULL); +} + +static XIC +get_ic(Ecore_IMF_Context *ctx) +{ + Ecore_IMF_Context_Data *imf_context_data; + XIC ic; + imf_context_data = ecore_imf_context_data_get(ctx); + ic = imf_context_data->ic; + if(!ic) + { + XIM_Im_Info *im_info = imf_context_data->im_info; + XVaNestedList preedit_attr = NULL; + XIMStyle im_style = 0; + char *name = NULL; + + if(imf_context_data->use_preedit == EINA_TRUE) + { + im_style |= XIMPreeditCallbacks; + preedit_attr = preedit_callback_set(ctx); + name = XNPreeditAttributes; + } + else { + im_style |= XIMPreeditNothing; + } + im_style |= XIMStatusNothing; + + ic = XCreateIC(im_info->im, + XNInputStyle, im_style, + XNClientWindow, imf_context_data->win, + name, preedit_attr, NULL); + XFree(preedit_attr); + if(ic) + { + unsigned long mask = 0xaaaaaaaa; + XGetICValues (ic, + XNFilterEvents, &mask, + NULL); + imf_context_data->mask = mask; + ecore_x_event_mask_set(imf_context_data->win, mask); + } + + imf_context_data->ic = ic; + if(ic && imf_context_data->has_focus == EINA_TRUE) + XSetICFocus(ic); + } + + return ic; +} + +static void +reinitialize_ic(Ecore_IMF_Context *ctx) +{ + Ecore_IMF_Context_Data *imf_context_data = ecore_imf_context_data_get(ctx); + XIC ic = imf_context_data->ic; + if(ic) + { + XDestroyIC(ic); + imf_context_data->ic = NULL; + if(imf_context_data->preedit_length) + { + imf_context_data->preedit_length = 0; + ecore_imf_context_preedit_changed_event_add(ctx); + } + } +} + +static void +reinitialize_all_ics(XIM_Im_Info *info) +{ + Eina_List *tmp_list; + Ecore_IMF_Context *ctx; + + EINA_LIST_FOREACH(info->ics, tmp_list, ctx) + reinitialize_ic(ctx); +} + +static void +set_ic_client_window(Ecore_IMF_Context *ctx, + Ecore_X_Window window) +{ + EINA_LOG_DBG("in"); + Ecore_IMF_Context_Data *imf_context_data = ecore_imf_context_data_get(ctx); + Ecore_X_Window old_win; + + /* reinitialize IC */ + reinitialize_ic(ctx); + + old_win = imf_context_data->win; + EINA_LOG_DBG("old_win:%d window:%d ", old_win, window); + if(old_win != 0 && old_win != window) /* XXX how do check window... */ + { + XIM_Im_Info *info; + info = imf_context_data->im_info; + info->ics = eina_list_remove(info->ics, imf_context_data); + imf_context_data->im_info = NULL; + } + + imf_context_data->win = window; + + if(window) /* XXX */ + { + XIM_Im_Info *info = NULL; + info = get_im(window, imf_context_data->locale); + imf_context_data->im_info = info; + imf_context_data->im_info->ics = + eina_list_prepend(imf_context_data->im_info->ics, + imf_context_data); + } +} + +static XIM_Im_Info * +get_im(Ecore_X_Window window, + char *locale) +{ + EINA_LOG_DBG("in"); + + Eina_List *l; + XIM_Im_Info *im_info = NULL; + XIM_Im_Info *info = NULL; + EINA_LIST_FOREACH(open_ims, l, im_info) { + if(strcmp(im_info->locale, locale) == 0) + { + if(im_info->im) + { + return im_info; + } + else { + info = im_info; + break; + } + } + } + + if(!info) + { + info = calloc(1, sizeof(XIM_Im_Info)); + if(!info) return NULL; + open_ims = eina_list_prepend(open_ims, info); + info->win = window; + info->locale = strdup(locale); + info->reconnecting = EINA_FALSE; + } + + xim_info_try_im(info); + return info; +} + +/* initialize info->im */ +static void +xim_info_try_im(XIM_Im_Info *info) +{ + Ecore_X_Display *dsp; + + assert(info->im == NULL); + if (info->reconnecting == EINA_TRUE) + return; + + if(XSupportsLocale()) + { + if (!XSetLocaleModifiers ("")) + EINA_LOG_WARN("Unable to set locale modifiers with XSetLocaleModifiers()"); + dsp = ecore_x_display_get(); + info->im = XOpenIM(dsp, NULL, NULL, NULL); + if(!info->im) + { + XRegisterIMInstantiateCallback(dsp, + NULL, NULL, NULL, + xim_instantiate_callback, + (XPointer)info); + info->reconnecting = EINA_TRUE; + return; + } + setup_im(info); + } +} + +static void +xim_info_display_closed(Ecore_X_Display *display, + int is_error, + XIM_Im_Info *info) +{ + Eina_List *ics, *tmp_list; + Ecore_IMF_Context *ctx; + + open_ims = eina_list_remove(open_ims, info); + + ics = info->ics; + info->ics = NULL; + + EINA_LIST_FOREACH(ics, tmp_list, ctx) + set_ic_client_window(ctx, 0); + + EINA_LIST_FREE(ics, ctx) { + Ecore_IMF_Context_Data *imf_context_data; + imf_context_data = ecore_imf_context_data_get(ctx); + imf_context_data_destroy(imf_context_data); + } + + free (info->locale); + + if (info->im) + XCloseIM (info->im); + + free (info); +} + +static void +xim_instantiate_callback(Display *display, + XPointer client_data, + XPointer call_data) +{ + XIM_Im_Info *info = (XIM_Im_Info *)client_data; + XIM im = NULL; + + im = XOpenIM(display, NULL, NULL, NULL); + + if(!im) + return; + + info->im = im; + setup_im (info); + + XUnregisterIMInstantiateCallback (display, NULL, NULL, NULL, + xim_instantiate_callback, + (XPointer)info); + info->reconnecting = EINA_FALSE; +} + +static void +setup_im(XIM_Im_Info *info) +{ + XIMValuesList *ic_values = NULL; + XIMCallback im_destroy_callback; + + if(!info->im) + return; + + im_destroy_callback.client_data = (XPointer)info; + im_destroy_callback.callback = (XIMProc)xim_destroy_callback; + XSetIMValues(info->im, + XNDestroyCallback, &im_destroy_callback, + NULL); + + XGetIMValues(info->im, + XNQueryInputStyle, &info->xim_styles, + XNQueryICValuesList, &ic_values, + NULL); + + if(ic_values) + { + int i; + + for(i = 0; i < ic_values->count_values; i++) + if(strcmp (ic_values->supported_values[i], + XNStringConversionCallback) == 0) + { + // info->supports_string_conversion = EINA_TRUE; + break; + } +#if 0 + for(i = 0; i < ic_values->count_values; i++) + printf("%s\n", ic_values->supported_values[i]); + for(i = 0; i < info->xim_styles->count_styles; i++) + printf("%lx\n", info->xim_styles->supported_styles[i]); +#endif + XFree(ic_values); + } +} + +static void +xim_destroy_callback(XIM xim, + XPointer client_data, + XPointer call_data) +{ + XIM_Im_Info *info = (XIM_Im_Info *)client_data; + info->im = NULL; + + reinitialize_all_ics(info); + xim_info_try_im(info); + + return; +} + +#endif /* ENABLE_XIM */