ecore-x: Add XIM module for ecore_imf
authormike_m <mike_m@7cbeb6ba-43b4-40fd-8cce-4c39aea84d33>
Tue, 12 Jul 2011 02:26:39 +0000 (02:26 +0000)
committermike_m <mike_m@7cbeb6ba-43b4-40fd-8cce-4c39aea84d33>
Tue, 12 Jul 2011 02:26:39 +0000 (02:26 +0000)
Signed-off-by: Naruto TAKAHASHI <tnaruto@gmail.com>
git-svn-id: svn+ssh://svn.enlightenment.org/var/svn/e/trunk/ecore@61259 7cbeb6ba-43b4-40fd-8cce-4c39aea84d33

src/modules/immodules/xim/Makefile.am [new file with mode: 0644]
src/modules/immodules/xim/ecore_imf_xim.c [new file with mode: 0644]

diff --git a/src/modules/immodules/xim/Makefile.am b/src/modules/immodules/xim/Makefile.am
new file mode 100644 (file)
index 0000000..d84cab2
--- /dev/null
@@ -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/src/modules/immodules/xim/ecore_imf_xim.c b/src/modules/immodules/xim/ecore_imf_xim.c
new file mode 100644 (file)
index 0000000..5b3b060
--- /dev/null
@@ -0,0 +1,1150 @@
+#include <Eina.h>
+#include <Ecore.h>
+#include <Ecore_Input.h>
+#include <Ecore_IMF.h>
+#include <Evas.h>
+#include <Ecore_X.h>
+#include <X11/Xlib.h>
+#include <X11/Xlocale.h>
+#include <X11/Xutil.h>
+#include <X11/keysym.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <langinfo.h>
+#include <assert.h>
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#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 */