Tizen 2.1 base
[framework/uifw/ecore.git] / src / modules / immodules / xim / ecore_imf_xim.c
1 #ifdef HAVE_CONFIG_H
2 # include <config.h>
3 #endif
4
5 #include <Eina.h>
6 #include <Ecore.h>
7 #include <Ecore_Input.h>
8 #include <Ecore_IMF.h>
9 #include <Ecore_X.h>
10 #include <X11/Xlib.h>
11 #include <X11/Xlocale.h>
12 #include <X11/Xutil.h>
13 #include <stdio.h>
14 #include <stdlib.h>
15 #include <string.h>
16 #include <langinfo.h>
17 #include <assert.h>
18
19 #define CLAMP(x, low, high) (x > high) ? high : (x < low) ? low : x
20 #define _(x)                x
21
22 #ifdef ENABLE_XIM
23 static Eina_List *open_ims = NULL;
24 #endif
25
26 #define FEEDBACK_MASK (XIMReverse | XIMUnderline | XIMHighlight)
27
28 typedef struct _XIM_Im_Info XIM_Im_Info;
29
30 typedef struct _Ecore_IMF_Context_Data Ecore_IMF_Context_Data;
31
32 struct _XIM_Im_Info
33 {
34    Ecore_X_Window win;
35    Ecore_IMF_Context_Data *user;
36    char          *locale;
37    XIM            im;
38    Eina_List     *ics;
39    Eina_Bool      reconnecting;
40    XIMStyles     *xim_styles;
41    Eina_Bool      supports_string_conversion : 1;
42    Eina_Bool      supports_cursor : 1;
43 };
44
45 struct _Ecore_IMF_Context_Data
46 {
47    Ecore_X_Window win;
48    long           mask;
49    XIC            ic; /* Input context for composed characters */
50    char          *locale;
51    XIM_Im_Info   *im_info;
52    int            preedit_length;
53    int            preedit_cursor;
54    Eina_Unicode  *preedit_chars;
55    Eina_Bool      use_preedit;
56    Eina_Bool      finalizing;
57    Eina_Bool      has_focus;
58    Eina_Bool      in_toplevel;
59    XIMFeedback   *feedbacks;
60
61    XIMCallback    destroy_cb;
62
63    XIMCallback    preedit_start_cb;
64    XIMCallback    preedit_done_cb;
65    XIMCallback    preedit_draw_cb;
66    XIMCallback    preedit_caret_cb;
67 };
68
69 /* prototype */
70 Ecore_IMF_Context_Data *imf_context_data_new();
71 void                    imf_context_data_destroy(Ecore_IMF_Context_Data *imf_context_data);
72
73 #ifdef ENABLE_XIM
74 static void          add_feedback_attr(Eina_List **attrs,
75                                        const char *str,
76                                        XIMFeedback feedback,
77                                        int start_pos,
78                                        int end_pos);
79
80 static void          reinitialize_ic(Ecore_IMF_Context *ctx);
81 static void          set_ic_client_window(Ecore_IMF_Context *ctx,
82                                           Ecore_X_Window window);
83 static int           preedit_start_callback(XIC xic,
84                                             XPointer client_data,
85                                             XPointer call_data);
86 static void          preedit_done_callback(XIC xic,
87                                            XPointer client_data,
88                                            XPointer call_data);
89 static int           xim_text_to_utf8(Ecore_IMF_Context *ctx,
90                                       XIMText *xim_text,
91                                       char **text);
92 static void          preedit_draw_callback(XIC xic,
93                                            XPointer client_data,
94                                            XIMPreeditDrawCallbackStruct *call_data);
95 static void          preedit_caret_callback(XIC xic,
96                                             XPointer client_data,
97                                             XIMPreeditCaretCallbackStruct *call_data);
98 static XVaNestedList preedit_callback_set(Ecore_IMF_Context *ctx);
99 static XIC           get_ic(Ecore_IMF_Context *ctx);
100 static XIM_Im_Info  *get_im(Ecore_X_Window window,
101                             char *locale);
102 static void          xim_info_try_im(XIM_Im_Info *info);
103 static void          xim_info_display_closed(Ecore_X_Display *display,
104                                              int is_error,
105                                              XIM_Im_Info *info);
106 static void          xim_instantiate_callback(Display *display,
107                                               XPointer client_data,
108                                               XPointer call_data);
109 static void          setup_im(XIM_Im_Info *info);
110 static void          xim_destroy_callback(XIM xim,
111                                           XPointer client_data,
112                                           XPointer call_data);
113 #endif
114
115 #ifdef ENABLE_XIM
116 static unsigned int
117 utf8_offset_to_index(const char *str, int offset)
118 {
119    int idx = 0;
120    int i;
121    for (i = 0; i < offset; i++)
122      {
123         eina_unicode_utf8_get_next(str, &idx);
124      }
125
126    return idx;
127 }
128
129 #endif
130
131 static void
132 _ecore_imf_context_xim_add(Ecore_IMF_Context *ctx)
133 {
134    EINA_LOG_DBG("in");
135 #ifdef ENABLE_XIM
136    Ecore_IMF_Context_Data *imf_context_data = NULL;
137
138    imf_context_data = imf_context_data_new();
139    EINA_SAFETY_ON_NULL_RETURN(imf_context_data);
140
141    imf_context_data->use_preedit = EINA_TRUE;
142    imf_context_data->finalizing = EINA_FALSE;
143    imf_context_data->has_focus = EINA_FALSE;
144    imf_context_data->in_toplevel = EINA_FALSE;
145
146    ecore_imf_context_data_set(ctx, imf_context_data);
147 #else
148    (void)ctx;
149 #endif
150 }
151
152 static void
153 _ecore_imf_context_xim_del(Ecore_IMF_Context *ctx)
154 {
155    EINA_LOG_DBG("in");
156 #ifdef ENABLE_XIM
157    Ecore_IMF_Context_Data *imf_context_data;
158    imf_context_data = ecore_imf_context_data_get(ctx);
159    EINA_SAFETY_ON_NULL_RETURN(imf_context_data);
160
161    imf_context_data->finalizing = EINA_TRUE;
162    if (imf_context_data->im_info && !imf_context_data->im_info->ics->next)
163      {
164         if (imf_context_data->im_info->reconnecting == EINA_TRUE)
165           {
166              Ecore_X_Display *dsp;
167              dsp = ecore_x_display_get();
168              XUnregisterIMInstantiateCallback(dsp,
169                                               NULL, NULL, NULL,
170                                               xim_instantiate_callback,
171                                               (XPointer)imf_context_data->im_info);
172           }
173         else if (imf_context_data->im_info->im)
174           {
175              XIMCallback im_destroy_callback;
176              im_destroy_callback.client_data = NULL;
177              im_destroy_callback.callback = NULL;
178              XSetIMValues(imf_context_data->im_info->im,
179                           XNDestroyCallback, &im_destroy_callback,
180                           NULL);
181           }
182      }
183
184    set_ic_client_window(ctx, 0);
185
186    imf_context_data_destroy(imf_context_data);
187 #else
188    (void)ctx;
189 #endif
190 }
191
192 static void
193 _ecore_imf_context_xim_client_window_set(Ecore_IMF_Context *ctx,
194                                          void *window)
195 {
196    EINA_LOG_DBG("in");
197 #ifdef ENABLE_XIM
198    set_ic_client_window(ctx, (Ecore_X_Window)((Ecore_Window)window));
199 #else
200    (void)ctx;
201    (void)window;
202 #endif
203 }
204
205 static void
206 _ecore_imf_context_xim_preedit_string_get(Ecore_IMF_Context *ctx,
207                                           char **str,
208                                           int *cursor_pos)
209 {
210    EINA_LOG_DBG("in");
211 #ifdef ENABLE_XIM
212    Ecore_IMF_Context_Data *imf_context_data;
213    char *utf8;
214    int len;
215    imf_context_data = ecore_imf_context_data_get(ctx);
216    EINA_SAFETY_ON_NULL_RETURN(imf_context_data);
217
218    if (imf_context_data->preedit_chars)
219      {
220         utf8 = eina_unicode_unicode_to_utf8(imf_context_data->preedit_chars,
221                                             &len);
222         if (str)
223           *str = utf8;
224         else
225           free(utf8);
226      }
227    else
228      {
229         if (str)
230           *str = NULL;
231         if (cursor_pos)
232           *cursor_pos = 0;
233      }
234
235    if (cursor_pos)
236      *cursor_pos = imf_context_data->preedit_cursor;
237 #else
238    (void)ctx;
239    if (str)
240      *str = NULL;
241    if (cursor_pos)
242      *cursor_pos = 0;
243 #endif
244 }
245
246 static void
247 _ecore_imf_context_xim_preedit_string_with_attributes_get(Ecore_IMF_Context *ctx,
248                                                           char **str,
249                                                           Eina_List **attrs,
250                                                           int *cursor_pos)
251 {
252    EINA_LOG_DBG("in");
253
254 #ifdef ENABLE_XIM
255    Ecore_IMF_Context_Data *imf_context_data = ecore_imf_context_data_get(ctx);
256
257    _ecore_imf_context_xim_preedit_string_get(ctx, str, cursor_pos);
258
259    if (!attrs) return;
260    if (!imf_context_data || !imf_context_data->feedbacks) return;
261
262    int i = 0;
263    XIMFeedback last_feedback = 0;
264    int start = -1;
265
266    for (i = 0; i < imf_context_data->preedit_length; i++)
267      {
268         XIMFeedback new_feedback = imf_context_data->feedbacks[i] & FEEDBACK_MASK;
269
270         if (new_feedback != last_feedback)
271           {
272              if (start >= 0)
273                add_feedback_attr(attrs, *str, last_feedback, start, i);
274
275              last_feedback = new_feedback;
276              start = i;
277           }
278      }
279
280    if (start >= 0)
281      add_feedback_attr(attrs, *str, last_feedback, start, i);
282 #else
283    (void)ctx;
284    if (str)
285      *str = NULL;
286    if (attrs)
287      *attrs = NULL;
288    if (cursor_pos)
289      *cursor_pos = 0;
290 #endif
291 }
292
293 static void
294 _ecore_imf_context_xim_focus_in(Ecore_IMF_Context *ctx)
295 {
296    EINA_LOG_DBG("in");
297 #ifdef ENABLE_XIM
298    XIC ic;
299    Ecore_IMF_Context_Data *imf_context_data;
300    imf_context_data = ecore_imf_context_data_get(ctx);
301    EINA_SAFETY_ON_NULL_RETURN(imf_context_data);
302
303    ic = imf_context_data->ic;
304    imf_context_data->has_focus = EINA_TRUE;
305
306    if (ecore_imf_context_input_panel_enabled_get(ctx))
307      ecore_imf_context_input_panel_show(ctx);
308
309    if (ic)
310      {
311         char *str;
312
313 #ifdef X_HAVE_UTF8_STRING
314         if ((str = Xutf8ResetIC(ic)))
315 #else
316         if ((str = XmbResetIC(ic)))
317 #endif
318           XFree(str);
319
320         XSetICFocus(ic);
321      }
322 #else
323    (void)ctx;
324 #endif
325 }
326
327 static void
328 _ecore_imf_context_xim_focus_out(Ecore_IMF_Context *ctx)
329 {
330    EINA_LOG_DBG("%s in", __FUNCTION__);
331 #ifdef ENABLE_XIM
332    XIC ic;
333    Ecore_IMF_Context_Data *imf_context_data;
334    imf_context_data = ecore_imf_context_data_get(ctx);
335    EINA_SAFETY_ON_NULL_RETURN(imf_context_data);
336
337    if (imf_context_data->has_focus == EINA_TRUE)
338      {
339         imf_context_data->has_focus = EINA_FALSE;
340         ic = imf_context_data->ic;
341         if (ic)
342           XUnsetICFocus(ic);
343
344         if (ecore_imf_context_input_panel_enabled_get(ctx))
345           ecore_imf_context_input_panel_hide(ctx);
346      }
347 #else
348    (void)ctx;
349 #endif
350 }
351
352 static void
353 _ecore_imf_context_xim_reset(Ecore_IMF_Context *ctx)
354 {
355    EINA_LOG_DBG("%s in", __FUNCTION__);
356 #ifdef ENABLE_XIM
357    XIC ic;
358    Ecore_IMF_Context_Data *imf_context_data;
359    char *result;
360
361    /* restore conversion state after resetting ic later */
362    XIMPreeditState preedit_state = XIMPreeditUnKnown;
363    XVaNestedList preedit_attr;
364    Eina_Bool have_preedit_state = EINA_FALSE;
365
366    imf_context_data = ecore_imf_context_data_get(ctx);
367    EINA_SAFETY_ON_NULL_RETURN(imf_context_data);
368
369    ic = imf_context_data->ic;
370    if (!ic)
371      return;
372
373    if (imf_context_data->preedit_length == 0)
374      return;
375
376    preedit_attr = XVaCreateNestedList(0,
377                                       XNPreeditState, &preedit_state,
378                                       NULL);
379    if (!XGetICValues(ic,
380                      XNPreeditAttributes, preedit_attr,
381                      NULL))
382      have_preedit_state = EINA_TRUE;
383
384    XFree(preedit_attr);
385
386    result = XmbResetIC(ic);
387
388    preedit_attr = XVaCreateNestedList(0,
389                                       XNPreeditState, preedit_state,
390                                       NULL);
391    if (have_preedit_state)
392      XSetICValues(ic,
393                   XNPreeditAttributes, preedit_attr,
394                   NULL);
395
396    XFree(preedit_attr);
397
398    if (imf_context_data->feedbacks)
399      {
400         free(imf_context_data->feedbacks);
401         imf_context_data->feedbacks = NULL;
402      }
403
404    if (imf_context_data->preedit_length)
405      {
406         imf_context_data->preedit_length = 0;
407         free(imf_context_data->preedit_chars);
408         imf_context_data->preedit_chars = NULL;
409
410         ecore_imf_context_preedit_changed_event_add(ctx);
411         ecore_imf_context_event_callback_call(ctx, ECORE_IMF_CALLBACK_PREEDIT_CHANGED, NULL);
412      }
413
414    if (result)
415      {
416         char *result_utf8 = strdup(result);
417         if (result_utf8)
418           {
419              ecore_imf_context_commit_event_add(ctx, result_utf8);
420              ecore_imf_context_event_callback_call(ctx, ECORE_IMF_CALLBACK_COMMIT, result_utf8);
421              free(result_utf8);
422           }
423      }
424
425    XFree(result);
426 #else
427    (void)ctx;
428 #endif
429 }
430
431 static void
432 _ecore_imf_context_xim_use_preedit_set(Ecore_IMF_Context *ctx,
433                                        Eina_Bool use_preedit)
434 {
435    EINA_LOG_DBG("in");
436 #ifdef ENABLE_XIM
437    Ecore_IMF_Context_Data *imf_context_data;
438    imf_context_data = ecore_imf_context_data_get(ctx);
439    EINA_SAFETY_ON_NULL_RETURN(imf_context_data);
440
441    use_preedit = use_preedit != EINA_FALSE;
442
443    if (imf_context_data->use_preedit != use_preedit)
444      {
445         imf_context_data->use_preedit = use_preedit;
446         reinitialize_ic(ctx);
447      }
448 #else
449    (void)ctx;
450    (void)use_preedit;
451 #endif
452 }
453
454 #ifdef ENABLE_XIM
455 static void
456 add_feedback_attr(Eina_List **attrs,
457                   const char *str,
458                   XIMFeedback feedback,
459                   int start_pos,
460                   int end_pos)
461 {
462    Ecore_IMF_Preedit_Attr *attr = NULL;
463
464    unsigned int start_index = utf8_offset_to_index(str, start_pos);
465    unsigned int end_index = utf8_offset_to_index(str, end_pos);
466
467    if (feedback & FEEDBACK_MASK)
468      {
469         attr = (Ecore_IMF_Preedit_Attr *)calloc(1, sizeof(Ecore_IMF_Preedit_Attr));
470         attr->start_index = start_index;
471         attr->end_index = end_index;
472         *attrs = eina_list_append(*attrs, (void *)attr);
473      }
474
475    if (feedback & XIMUnderline)
476      attr->preedit_type = ECORE_IMF_PREEDIT_TYPE_SUB1;
477
478    if (feedback & XIMReverse)
479      attr->preedit_type = ECORE_IMF_PREEDIT_TYPE_SUB2;
480
481    if (feedback & XIMHighlight)
482      attr->preedit_type = ECORE_IMF_PREEDIT_TYPE_SUB3;
483 }
484
485 #endif
486
487 static void
488 _ecore_imf_context_xim_cursor_location_set(Ecore_IMF_Context *ctx,
489                                            int x, int y, int w, int h)
490 {
491    EINA_LOG_DBG("%s in", __FUNCTION__);
492
493 #ifdef ENABLE_XIM
494    Ecore_IMF_Context_Data *imf_context_data;
495    XIC ic;
496    XVaNestedList preedit_attr;
497    XPoint spot;
498
499    imf_context_data = ecore_imf_context_data_get(ctx);
500    EINA_SAFETY_ON_NULL_RETURN(imf_context_data);
501    ic = imf_context_data->ic;
502    if (!ic)
503      return;
504
505    spot.x = x;
506    spot.y = y + h;
507
508    preedit_attr = XVaCreateNestedList(0,
509                                       XNSpotLocation, &spot,
510                                       NULL);
511    XSetICValues(ic,
512                 XNPreeditAttributes, preedit_attr,
513                 NULL);
514
515    XFree(preedit_attr);
516 #else
517    (void)ctx;
518    (void)x;
519    (void)y;
520    (void)h;
521 #endif
522    (void)(w); // yes w is unused, but only a bi-product of the algorithm
523 }
524
525 static void
526 _ecore_imf_context_xim_input_panel_show(Ecore_IMF_Context *ctx)
527 {
528    EINA_LOG_DBG("%s in", __FUNCTION__);
529
530 #ifdef ENABLE_XIM
531    Ecore_IMF_Context_Data *imf_context_data;
532    imf_context_data = ecore_imf_context_data_get(ctx);
533    EINA_SAFETY_ON_NULL_RETURN(imf_context_data);
534
535    ecore_x_e_virtual_keyboard_state_set
536         (imf_context_data->win, ECORE_X_VIRTUAL_KEYBOARD_STATE_ON);
537 #else
538    (void)ctx;
539 #endif
540 }
541
542 static void
543 _ecore_imf_context_xim_input_panel_hide(Ecore_IMF_Context *ctx)
544 {
545    EINA_LOG_DBG("%s in", __FUNCTION__);
546
547 #ifdef ENABLE_XIM
548    Ecore_IMF_Context_Data *imf_context_data;
549    imf_context_data = ecore_imf_context_data_get(ctx);
550    EINA_SAFETY_ON_NULL_RETURN(imf_context_data);
551
552    ecore_x_e_virtual_keyboard_state_set
553         (imf_context_data->win, ECORE_X_VIRTUAL_KEYBOARD_STATE_OFF);
554 #else
555    (void)ctx;
556 #endif
557 }
558
559 #ifdef ENABLE_XIM
560 static unsigned int
561 _ecore_x_event_reverse_modifiers(unsigned int state)
562 {
563    unsigned int modifiers = 0;
564
565    /**< "Control" is pressed */
566    if (state & ECORE_IMF_KEYBOARD_MODIFIER_CTRL)
567      modifiers |= ECORE_X_MODIFIER_CTRL;
568
569    /**< "Alt" is pressed */
570    if (state & ECORE_IMF_KEYBOARD_MODIFIER_ALT)
571      modifiers |= ECORE_X_MODIFIER_ALT;
572
573    /**< "Shift" is pressed */
574    if (state & ECORE_IMF_KEYBOARD_MODIFIER_SHIFT)
575      modifiers |= ECORE_X_MODIFIER_SHIFT;
576
577    /**< "Win" (between "Ctrl" and "Alt") is pressed */
578    if (state & ECORE_IMF_KEYBOARD_MODIFIER_WIN)
579      modifiers |= ECORE_X_MODIFIER_WIN;
580
581    /**< "AltGr" is pressed */
582    if (state & ECORE_IMF_KEYBOARD_MODIFIER_ALTGR)
583      modifiers |= ECORE_X_MODIFIER_ALTGR;
584
585    return modifiers;
586 }
587
588 static unsigned int
589 _ecore_x_event_reverse_locks(unsigned int state)
590 {
591    unsigned int locks = 0;
592
593    /**< "Num" lock is active */
594    if (state & ECORE_IMF_KEYBOARD_LOCK_NUM)
595      locks |= ECORE_X_LOCK_NUM;
596
597    if (state & ECORE_IMF_KEYBOARD_LOCK_CAPS)
598      locks |= ECORE_X_LOCK_CAPS;
599
600    if (state & ECORE_IMF_KEYBOARD_LOCK_SCROLL)
601      locks |= ECORE_X_LOCK_SCROLL;
602
603    return locks;
604 }
605
606 static KeyCode
607 _keycode_get(Ecore_X_Display *dsp,
608              const char *keyname)
609 {
610    KeyCode keycode;
611
612    // EINA_LOG_DBG("keyname:%s keysym:%lu", keyname, XStringToKeysym(keyname));
613    if (strcmp(keyname, "Keycode-0") == 0)
614      keycode = 0;
615    else
616      keycode = XKeysymToKeycode(dsp, XStringToKeysym(keyname));
617
618    return keycode;
619 }
620
621 #endif
622
623 static Eina_Bool
624 _ecore_imf_context_xim_filter_event(Ecore_IMF_Context *ctx,
625                                     Ecore_IMF_Event_Type type,
626                                     Ecore_IMF_Event *event)
627 {
628    EINA_LOG_DBG("%s in", __FUNCTION__);
629 #ifdef ENABLE_XIM
630    Ecore_IMF_Context_Data *imf_context_data;
631    XIC ic;
632
633    Ecore_X_Display *dsp;
634    Ecore_X_Window win;
635
636    int val;
637    char compose_buffer[256];
638    KeySym sym;
639    char *compose = NULL;
640    char *tmp = NULL;
641    Eina_Bool result = EINA_FALSE;
642
643    imf_context_data = ecore_imf_context_data_get(ctx);
644    if (!imf_context_data) return EINA_FALSE;
645    ic = imf_context_data->ic;
646    if (!ic)
647      ic = get_ic(ctx);
648
649    if (type == ECORE_IMF_EVENT_KEY_DOWN)
650      {
651         XKeyPressedEvent xev;
652         Ecore_IMF_Event_Key_Down *ev = (Ecore_IMF_Event_Key_Down *)event;
653         EINA_LOG_DBG("ECORE_IMF_EVENT_KEY_DOWN");
654
655         dsp = ecore_x_display_get();
656         win = imf_context_data->win;
657
658         xev.type = KeyPress;
659         xev.serial = 0; /* hope it doesn't matter */
660         xev.send_event = 0;
661         xev.display = dsp;
662         xev.window = win;
663         xev.root = ecore_x_window_root_get(win);
664         xev.subwindow = win;
665         xev.time = ev->timestamp;
666         xev.x = xev.x_root = 0;
667         xev.y = xev.y_root = 0;
668         xev.state = 0;
669         xev.state |= _ecore_x_event_reverse_modifiers(ev->modifiers);
670         xev.state |= _ecore_x_event_reverse_locks(ev->locks);
671         xev.keycode = _keycode_get(dsp, ev->keyname);
672         xev.same_screen = True;
673
674         if (ic)
675           {
676              Status mbstatus;
677 #ifdef X_HAVE_UTF8_STRING
678              val = Xutf8LookupString(ic,
679                                      &xev,
680                                      compose_buffer,
681                                      sizeof(compose_buffer) - 1,
682                                      &sym,
683                                      &mbstatus);
684 #else /* ifdef X_HAVE_UTF8_STRING */
685              val = XmbLookupString(ic,
686                                    &xev,
687                                    compose_buffer,
688                                    sizeof(compose_buffer) - 1,
689                                    &sym,
690                                    &mbstatus);
691 #endif /* ifdef X_HAVE_UTF8_STRING */
692              if (mbstatus == XBufferOverflow)
693                {
694                   tmp = malloc(sizeof (char) * (val + 1));
695                   if (!tmp)
696                     return EINA_FALSE;
697
698                   compose = tmp;
699
700 #ifdef X_HAVE_UTF8_STRING
701                   val = Xutf8LookupString(ic,
702                                           &xev,
703                                           tmp,
704                                           val,
705                                           &sym,
706                                           &mbstatus);
707 #else /* ifdef X_HAVE_UTF8_STRING */
708                   val = XmbLookupString(ic,
709                                         &xev,
710                                         tmp,
711                                         val,
712                                         &sym,
713                                         &mbstatus);
714 #endif /* ifdef X_HAVE_UTF8_STRING */
715                   if (val > 0)
716                     {
717                        tmp[val] = '\0';
718 #ifndef X_HAVE_UTF8_STRING
719                        compose = eina_str_convert(nl_langinfo(CODESET),
720                                                   "UTF-8", tmp);
721                        free(tmp);
722                        tmp = compose;
723 #endif /* ifndef X_HAVE_UTF8_STRING */
724                     }
725                   else
726                     compose = NULL;
727                }
728              else if (val > 0)
729                {
730                   compose_buffer[val] = '\0';
731 #ifdef X_HAVE_UTF8_STRING
732                   compose = strdup(compose_buffer);
733 #else /* ifdef X_HAVE_UTF8_STRING */
734                   compose = eina_str_convert(nl_langinfo(CODESET), "UTF-8",
735                                              compose_buffer);
736 #endif /* ifdef X_HAVE_UTF8_STRING */
737                }
738           }
739         else
740           {
741              compose = strdup(ev->compose);
742           }
743
744         if (compose)
745           {
746              Eina_Unicode *unicode;
747              int len;
748              unicode = eina_unicode_utf8_to_unicode(compose, &len);
749              if (!unicode) abort();
750              if (unicode[0] >= 0x20 && unicode[0] != 0x7f)
751                {
752                   ecore_imf_context_commit_event_add(ctx, compose);
753                   ecore_imf_context_event_callback_call(ctx, ECORE_IMF_CALLBACK_COMMIT, compose);
754                   result = EINA_TRUE;
755                }
756              free(compose);
757              free(unicode);
758           }
759      }
760
761    return result;
762 #else
763    (void)ctx;
764    (void)type;
765    (void)event;
766    return EINA_FALSE;
767 #endif
768 }
769
770 static const Ecore_IMF_Context_Info xim_info = {
771    .id = "xim",
772    .description = _("X input method"),
773    .default_locales = "ko:ja:th:zh",
774    .canvas_type = "evas",
775    .canvas_required = 1,
776 };
777
778 static Ecore_IMF_Context_Class xim_class = {
779    .add = _ecore_imf_context_xim_add,
780    .del = _ecore_imf_context_xim_del,
781    .client_window_set = _ecore_imf_context_xim_client_window_set,
782    .client_canvas_set = NULL,
783    .show = _ecore_imf_context_xim_input_panel_show,
784    .hide = _ecore_imf_context_xim_input_panel_hide,
785    .preedit_string_get = _ecore_imf_context_xim_preedit_string_get,
786    .focus_in = _ecore_imf_context_xim_focus_in,
787    .focus_out = _ecore_imf_context_xim_focus_out,
788    .reset = _ecore_imf_context_xim_reset,
789    .cursor_position_set = NULL,
790    .use_preedit_set = _ecore_imf_context_xim_use_preedit_set,
791    .input_mode_set = NULL,
792    .filter_event = _ecore_imf_context_xim_filter_event,
793    .preedit_string_with_attributes_get = _ecore_imf_context_xim_preedit_string_with_attributes_get,
794    .prediction_allow_set = NULL,
795    .autocapital_type_set = NULL,
796    .control_panel_show = NULL,
797    .control_panel_hide = NULL,
798    .input_panel_layout_set = NULL,
799    .input_panel_layout_get = NULL,
800    .input_panel_language_set = NULL,
801    .input_panel_language_get = NULL,
802    .cursor_location_set = _ecore_imf_context_xim_cursor_location_set,
803    .input_panel_imdata_set = NULL,
804    .input_panel_imdata_get = NULL,
805    .input_panel_return_key_type_set = NULL,
806    .input_panel_return_key_disabled_set = NULL,
807    .input_panel_caps_lock_mode_set = NULL
808 };
809
810 static Ecore_IMF_Context *
811 xim_imf_module_create(void)
812 {
813    EINA_LOG_DBG("%s in", __FUNCTION__);
814    Ecore_IMF_Context *ctx = NULL;
815
816    ctx = ecore_imf_context_new(&xim_class);
817    if (!ctx)
818      goto error;
819
820    return ctx;
821
822 error:
823    free(ctx);
824    return NULL;
825 }
826
827 static Ecore_IMF_Context *
828 xim_imf_module_exit(void)
829 {
830    return NULL;
831 }
832
833 Eina_Bool
834 ecore_imf_xim_init(void)
835 {
836    EINA_LOG_DBG("%s in", __FUNCTION__);
837    eina_init();
838    ecore_x_init(NULL);
839    ecore_imf_module_register(&xim_info,
840                              xim_imf_module_create,
841                              xim_imf_module_exit);
842
843    return EINA_TRUE;
844 }
845
846 void
847 ecore_imf_xim_shutdown(void)
848 {
849 #ifdef ENABLE_XIM
850    while (open_ims)
851      {
852         XIM_Im_Info *info = open_ims->data;
853         Ecore_X_Display *display = ecore_x_display_get();
854
855         xim_info_display_closed(display, EINA_FALSE, info);
856      }
857 #endif
858
859    ecore_x_shutdown();
860    eina_shutdown();
861 }
862
863 EINA_MODULE_INIT(ecore_imf_xim_init);
864 EINA_MODULE_SHUTDOWN(ecore_imf_xim_shutdown);
865
866 #ifdef ENABLE_XIM
867 /*
868  * internal functions
869  */
870 Ecore_IMF_Context_Data *
871 imf_context_data_new()
872 {
873    Ecore_IMF_Context_Data *imf_context_data = NULL;
874    char *locale;
875
876    locale = setlocale(LC_CTYPE, "");
877    if (!locale) return NULL;
878
879    if (!XSupportsLocale()) return NULL;
880
881    imf_context_data = calloc(1, sizeof(Ecore_IMF_Context_Data));
882    EINA_SAFETY_ON_NULL_RETURN_VAL(imf_context_data, NULL);
883
884    imf_context_data->locale = strdup(locale);
885    if (!imf_context_data->locale) goto error;
886
887    return imf_context_data;
888 error:
889    imf_context_data_destroy(imf_context_data);
890    return NULL;
891 }
892
893 void
894 imf_context_data_destroy(Ecore_IMF_Context_Data *imf_context_data)
895 {
896    if (!imf_context_data)
897      return;
898
899    if (imf_context_data->ic)
900      XDestroyIC(imf_context_data->ic);
901
902    free(imf_context_data->preedit_chars);
903
904    if (imf_context_data->feedbacks)
905      {
906         free(imf_context_data->feedbacks);
907         imf_context_data->feedbacks = NULL;
908      }
909
910    free(imf_context_data->locale);
911    free(imf_context_data);
912 }
913
914 static int
915 preedit_start_callback(XIC xic __UNUSED__,
916                        XPointer client_data,
917                        XPointer call_data __UNUSED__)
918 {
919    EINA_LOG_DBG("in");
920    Ecore_IMF_Context *ctx = (Ecore_IMF_Context *)client_data;
921    Ecore_IMF_Context_Data *imf_context_data;
922    imf_context_data = ecore_imf_context_data_get(ctx);
923    if (!imf_context_data) return -1;
924
925    if (imf_context_data->finalizing == EINA_FALSE)
926      {
927         ecore_imf_context_preedit_start_event_add(ctx);
928         ecore_imf_context_event_callback_call(ctx, ECORE_IMF_CALLBACK_PREEDIT_START, NULL);
929      }
930    return -1;
931 }
932
933 static void
934 preedit_done_callback(XIC xic __UNUSED__,
935                       XPointer client_data,
936                       XPointer call_data __UNUSED__)
937 {
938    EINA_LOG_DBG("in");
939    Ecore_IMF_Context *ctx = (Ecore_IMF_Context *)client_data;
940    Ecore_IMF_Context_Data *imf_context_data;
941    imf_context_data = ecore_imf_context_data_get(ctx);
942    EINA_SAFETY_ON_NULL_RETURN(imf_context_data);
943
944    if (imf_context_data->preedit_length)
945      {
946         imf_context_data->preedit_length = 0;
947         free(imf_context_data->preedit_chars);
948         imf_context_data->preedit_chars = NULL;
949         ecore_imf_context_preedit_changed_event_add(ctx);
950         ecore_imf_context_event_callback_call(ctx, ECORE_IMF_CALLBACK_PREEDIT_CHANGED, NULL);
951      }
952
953    if (imf_context_data->finalizing == EINA_FALSE)
954      {
955         ecore_imf_context_preedit_end_event_add(ctx);
956         ecore_imf_context_event_callback_call(ctx, ECORE_IMF_CALLBACK_PREEDIT_END, NULL);
957      }
958 }
959
960 /* FIXME */
961 static int
962 xim_text_to_utf8(Ecore_IMF_Context *ctx __UNUSED__,
963                  XIMText *xim_text,
964                  char **text)
965 {
966    int text_length = 0;
967    char *result = NULL;
968
969    if (xim_text && xim_text->string.multi_byte)
970      {
971         if (xim_text->encoding_is_wchar)
972           {
973              EINA_LOG_WARN("Wide character return from Xlib not currently supported");
974              *text = NULL;
975              return 0;
976           }
977
978         /* XXX Convert to UTF-8 */
979         result = strdup(xim_text->string.multi_byte);
980         if (result)
981           {
982              text_length = eina_unicode_utf8_get_len(result);
983              if (text_length != xim_text->length)
984                {
985                   EINA_LOG_WARN("Size mismatch when converting text from input method: supplied length = %d\n, result length = %d", xim_text->length, text_length);
986                }
987           }
988         else
989           {
990              EINA_LOG_WARN("Error converting text from IM to UCS-4");
991              *text = NULL;
992              return 0;
993           }
994
995         *text = result;
996         return text_length;
997      }
998    else
999      {
1000         *text = NULL;
1001         return 0;
1002      }
1003 }
1004
1005 static void
1006 preedit_draw_callback(XIC xic __UNUSED__,
1007                       XPointer client_data,
1008                       XIMPreeditDrawCallbackStruct *call_data)
1009 {
1010    EINA_LOG_DBG("in");
1011    Eina_Bool ret = EINA_FALSE;
1012    Ecore_IMF_Context *ctx = (Ecore_IMF_Context *)client_data;
1013    Ecore_IMF_Context_Data *imf_context_data = ecore_imf_context_data_get(ctx);
1014    XIMText *t = call_data->text;
1015    char *tmp;
1016    Eina_Unicode *new_text = NULL;
1017    Eina_UStrbuf *preedit_bufs = NULL;
1018    int new_text_length;
1019    int i = 0;
1020
1021    EINA_SAFETY_ON_NULL_RETURN(imf_context_data);
1022
1023    preedit_bufs = eina_ustrbuf_new();
1024    if (imf_context_data->preedit_chars)
1025      {
1026         ret = eina_ustrbuf_append(preedit_bufs, imf_context_data->preedit_chars);
1027         if (ret == EINA_FALSE) goto done;
1028      }
1029
1030    new_text_length = xim_text_to_utf8(ctx, t, &tmp);
1031    if (tmp)
1032      {
1033         int tmp_len;
1034         new_text = eina_unicode_utf8_to_unicode((const char *)tmp, &tmp_len);
1035         free(tmp);
1036      }
1037
1038    if (t == NULL)
1039      {
1040         /* delete string */
1041         ret = eina_ustrbuf_remove(preedit_bufs,
1042                                   call_data->chg_first, call_data->chg_length);
1043      }
1044    else if (call_data->chg_length == 0)
1045      {
1046         /* insert string */
1047         ret = eina_ustrbuf_insert(preedit_bufs, new_text, call_data->chg_first);
1048      }
1049    else if (call_data->chg_length > 0)
1050      {
1051         /* replace string */
1052         ret = eina_ustrbuf_remove(preedit_bufs,
1053                                   call_data->chg_first, call_data->chg_length);
1054         if (ret == EINA_FALSE) goto done;
1055
1056         ret = eina_ustrbuf_insert_n(preedit_bufs, new_text,
1057                                     new_text_length, call_data->chg_first);
1058         if (ret == EINA_FALSE) goto done;
1059      }
1060    else
1061      {
1062         ret = EINA_FALSE;
1063      }
1064
1065 done:
1066    if (ret == EINA_TRUE)
1067      {
1068         free(imf_context_data->preedit_chars);
1069         imf_context_data->preedit_chars =
1070           eina_ustrbuf_string_steal(preedit_bufs);
1071         imf_context_data->preedit_length =
1072           eina_unicode_strlen(imf_context_data->preedit_chars);
1073
1074         if (imf_context_data->feedbacks)
1075           {
1076              free(imf_context_data->feedbacks);
1077              imf_context_data->feedbacks = NULL;
1078           }
1079
1080         if (imf_context_data->preedit_length > 0)
1081           {
1082              imf_context_data->feedbacks = calloc(imf_context_data->preedit_length, sizeof(XIMFeedback));
1083
1084              for (i = 0; i < imf_context_data->preedit_length; i++)
1085                imf_context_data->feedbacks[i] = t->feedback[i];
1086           }
1087
1088         ecore_imf_context_preedit_changed_event_add(ctx);
1089         ecore_imf_context_event_callback_call(ctx, ECORE_IMF_CALLBACK_PREEDIT_CHANGED, NULL);
1090      }
1091
1092    free(new_text);
1093    eina_ustrbuf_free(preedit_bufs);
1094 }
1095
1096 static void
1097 preedit_caret_callback(XIC xic __UNUSED__,
1098                        XPointer client_data,
1099                        XIMPreeditCaretCallbackStruct *call_data)
1100 {
1101    EINA_LOG_DBG("in");
1102    Ecore_IMF_Context *ctx = (Ecore_IMF_Context *)client_data;
1103    Ecore_IMF_Context_Data *imf_context_data;
1104    imf_context_data = ecore_imf_context_data_get(ctx);
1105    EINA_SAFETY_ON_NULL_RETURN(imf_context_data);
1106
1107    if (call_data->direction == XIMAbsolutePosition)
1108      {
1109         // printf("call_data->position:%d\n", call_data->position);
1110         imf_context_data->preedit_cursor = call_data->position;
1111         if (imf_context_data->finalizing == EINA_FALSE)
1112           {
1113              ecore_imf_context_preedit_changed_event_add(ctx);
1114              ecore_imf_context_event_callback_call(ctx, ECORE_IMF_CALLBACK_PREEDIT_CHANGED, NULL);
1115           }
1116      }
1117 }
1118
1119 static XVaNestedList
1120 preedit_callback_set(Ecore_IMF_Context *ctx)
1121 {
1122    Ecore_IMF_Context_Data *imf_context_data;
1123    imf_context_data = ecore_imf_context_data_get(ctx);
1124
1125    imf_context_data->preedit_start_cb.client_data = (XPointer)ctx;
1126    imf_context_data->preedit_start_cb.callback = (XIMProc)preedit_start_callback;
1127
1128    imf_context_data->preedit_done_cb.client_data = (XPointer)ctx;
1129    imf_context_data->preedit_done_cb.callback = (XIMProc)preedit_done_callback;
1130
1131    imf_context_data->preedit_draw_cb.client_data = (XPointer)ctx;
1132    imf_context_data->preedit_draw_cb.callback = (XIMProc)preedit_draw_callback;
1133
1134    imf_context_data->preedit_caret_cb.client_data = (XPointer)ctx;
1135    imf_context_data->preedit_caret_cb.callback = (XIMProc)preedit_caret_callback;
1136
1137    return XVaCreateNestedList(0,
1138                               XNPreeditStartCallback,
1139                               &imf_context_data->preedit_start_cb,
1140                               XNPreeditDoneCallback,
1141                               &imf_context_data->preedit_done_cb,
1142                               XNPreeditDrawCallback,
1143                               &imf_context_data->preedit_draw_cb,
1144                               XNPreeditCaretCallback,
1145                               &imf_context_data->preedit_caret_cb,
1146                               NULL);
1147 }
1148
1149 static XIC
1150 get_ic(Ecore_IMF_Context *ctx)
1151 {
1152    Ecore_IMF_Context_Data *imf_context_data;
1153    XIC ic;
1154    imf_context_data = ecore_imf_context_data_get(ctx);
1155    EINA_SAFETY_ON_NULL_RETURN_VAL(imf_context_data, NULL);
1156
1157    ic = imf_context_data->ic;
1158    if (!ic)
1159      {
1160         XIM_Im_Info *im_info = imf_context_data->im_info;
1161         XVaNestedList preedit_attr = NULL;
1162         XIMStyle im_style = 0;
1163         XPoint spot = { 0, 0 };
1164         char *name = NULL;
1165
1166         if (!im_info)
1167           {
1168              EINA_LOG_WARN("Doesn't open XIM.");
1169              return NULL;
1170           }
1171
1172         // supported styles
1173 #if 0
1174         int i;
1175         if (im_info->xim_styles)
1176           {
1177              for (i = 0; i < im_info->xim_styles->count_styles; i++)
1178                {
1179                   printf("%i: ", i);
1180                   if (im_info->xim_styles->supported_styles[i] & XIMPreeditCallbacks)
1181                     printf("XIMPreeditCallbacks | ");
1182                   if (im_info->xim_styles->supported_styles[i] & XIMPreeditPosition)
1183                     printf("XIMPreeditPosition | ");
1184                   if (im_info->xim_styles->supported_styles[i] & XIMPreeditArea)
1185                     printf("XIMPreeditArea | ");
1186                   if (im_info->xim_styles->supported_styles[i] & XIMPreeditNothing)
1187                     printf("XIMPreeditNothing | ");
1188                   if (im_info->xim_styles->supported_styles[i] & XIMPreeditNone)
1189                     printf("XIMPreeditNone | ");
1190                   if (im_info->xim_styles->supported_styles[i] & XIMStatusArea)
1191                     printf("XIMStatusArea | ");
1192                   if (im_info->xim_styles->supported_styles[i] & XIMStatusCallbacks)
1193                     printf("XIMStatusCallbacks | ");
1194                   if (im_info->xim_styles->supported_styles[i] & XIMStatusNothing)
1195                     printf("XIMStatusNothing | ");
1196                   if (im_info->xim_styles->supported_styles[i] & XIMStatusNone)
1197                     printf("XIMStatusNone | ");
1198                   printf("\n");
1199                }
1200           }
1201 #endif
1202         // "OverTheSpot" = XIMPreeditPosition | XIMStatusNothing
1203         // "OffTheSpot" = XIMPreeditArea | XIMStatusArea
1204         // "Root" = XIMPreeditNothing | XIMStatusNothing
1205
1206         if (imf_context_data->use_preedit == EINA_TRUE)
1207           {
1208              if (im_info->supports_cursor)
1209                {
1210                   // kinput2 DOES do this...
1211                   XFontSet fs;
1212                   char **missing_charset_list;
1213                   int missing_charset_count;
1214                   char *def_string;
1215
1216                   im_style |= XIMPreeditPosition;
1217                   im_style |= XIMStatusNothing;
1218                   fs = XCreateFontSet(ecore_x_display_get(),
1219                                       "fixed",
1220                                       &missing_charset_list,
1221                                       &missing_charset_count,
1222                                       &def_string);
1223                   preedit_attr = XVaCreateNestedList(0,
1224                                                      XNSpotLocation, &spot,
1225                                                      XNFontSet, fs,
1226                                                      NULL);
1227                }
1228              else
1229                {
1230                   im_style |= XIMPreeditCallbacks;
1231                   im_style |= XIMStatusNothing;
1232                   preedit_attr = preedit_callback_set(ctx);
1233                }
1234              name = XNPreeditAttributes;
1235           }
1236         else
1237           {
1238              im_style |= XIMPreeditNothing;
1239              im_style |= XIMStatusNothing;
1240           }
1241
1242         if (!im_info->xim_styles)
1243           {
1244              EINA_LOG_WARN("No XIM styles supported! Wanted %#llx",
1245                            (unsigned long long)im_style);
1246              im_style = 0;
1247           }
1248         else
1249           {
1250              XIMStyle fallback = 0;
1251              int i;
1252
1253              for (i = 0; i < im_info->xim_styles->count_styles; i++)
1254                {
1255                   XIMStyle cur = im_info->xim_styles->supported_styles[i];
1256                   if (cur == im_style)
1257                     break;
1258                   else if (cur == (XIMPreeditNothing | XIMStatusNothing))
1259                     /* TODO: fallback is just that or the anyone? */
1260                     fallback = cur;
1261                }
1262
1263              if (i == im_info->xim_styles->count_styles)
1264                {
1265                   if (fallback)
1266                     {
1267                        EINA_LOG_WARN("Wanted XIM style %#llx not found, "
1268                                      "using fallback %#llx instead.",
1269                                      (unsigned long long)im_style,
1270                                      (unsigned long long)fallback);
1271                        im_style = fallback;
1272                     }
1273                   else
1274                     {
1275                        EINA_LOG_WARN("Wanted XIM style %#llx not found, "
1276                                      "no fallback supported.",
1277                                      (unsigned long long)im_style);
1278                        im_style = 0;
1279                     }
1280                }
1281           }
1282
1283         if ((im_info->im) && (im_style))
1284           {
1285              ic = XCreateIC(im_info->im,
1286                             XNInputStyle, im_style,
1287                             XNClientWindow, imf_context_data->win,
1288                             name, preedit_attr, NULL);
1289           }
1290         XFree(preedit_attr);
1291         if (ic)
1292           {
1293              unsigned long mask = 0xaaaaaaaa;
1294              XGetICValues(ic,
1295                           XNFilterEvents, &mask,
1296                           NULL);
1297              imf_context_data->mask = mask;
1298              ecore_x_event_mask_set(imf_context_data->win, mask);
1299           }
1300
1301         imf_context_data->ic = ic;
1302         if (ic && imf_context_data->has_focus == EINA_TRUE)
1303           XSetICFocus(ic);
1304      }
1305
1306    return ic;
1307 }
1308
1309 static void
1310 reinitialize_ic(Ecore_IMF_Context *ctx)
1311 {
1312    Ecore_IMF_Context_Data *imf_context_data = ecore_imf_context_data_get(ctx);
1313    EINA_SAFETY_ON_NULL_RETURN(imf_context_data);
1314
1315    XIC ic = imf_context_data->ic;
1316    if (ic)
1317      {
1318         XDestroyIC(ic);
1319         imf_context_data->ic = NULL;
1320         if (imf_context_data->preedit_length)
1321           {
1322              imf_context_data->preedit_length = 0;
1323              free(imf_context_data->preedit_chars);
1324              imf_context_data->preedit_chars = NULL;
1325              ecore_imf_context_preedit_changed_event_add(ctx);
1326              ecore_imf_context_event_callback_call(ctx, ECORE_IMF_CALLBACK_PREEDIT_CHANGED, NULL);
1327           }
1328      }
1329 }
1330
1331 static void
1332 set_ic_client_window(Ecore_IMF_Context *ctx,
1333                      Ecore_X_Window window)
1334 {
1335    EINA_LOG_DBG("in");
1336    Ecore_IMF_Context_Data *imf_context_data = ecore_imf_context_data_get(ctx);
1337    Ecore_X_Window old_win;
1338
1339    EINA_SAFETY_ON_NULL_RETURN(imf_context_data);
1340
1341    /* reinitialize IC */
1342    reinitialize_ic(ctx);
1343
1344    old_win = imf_context_data->win;
1345    EINA_LOG_DBG("old_win:%d window:%d ", old_win, window);
1346    if (old_win != 0 && old_win != window)   /* XXX how do check window... */
1347      {
1348         XIM_Im_Info *info;
1349         info = imf_context_data->im_info;
1350         info->ics = eina_list_remove(info->ics, imf_context_data);
1351         if (imf_context_data->im_info)
1352           imf_context_data->im_info->user = NULL;
1353         imf_context_data->im_info = NULL;
1354      }
1355
1356    imf_context_data->win = window;
1357
1358    if (window) /* XXX */
1359      {
1360         XIM_Im_Info *info = NULL;
1361         info = get_im(window, imf_context_data->locale);
1362         imf_context_data->im_info = info;
1363         imf_context_data->im_info->ics =
1364           eina_list_prepend(imf_context_data->im_info->ics,
1365                             imf_context_data);
1366         if (imf_context_data->im_info)
1367           imf_context_data->im_info->user = imf_context_data;
1368      }
1369 }
1370
1371 static XIM_Im_Info *
1372 get_im(Ecore_X_Window window,
1373        char *locale)
1374 {
1375    EINA_LOG_DBG("in");
1376
1377    Eina_List *l;
1378    XIM_Im_Info *im_info = NULL;
1379    XIM_Im_Info *info = NULL;
1380    EINA_LIST_FOREACH (open_ims, l, im_info)
1381      {
1382         if (strcmp(im_info->locale, locale) == 0)
1383           {
1384              if (im_info->im)
1385                {
1386                   return im_info;
1387                }
1388              else
1389                {
1390                   info = im_info;
1391                   break;
1392                }
1393           }
1394      }
1395
1396    if (!info)
1397      {
1398         info = calloc(1, sizeof(XIM_Im_Info));
1399         if (!info) return NULL;
1400         open_ims = eina_list_prepend(open_ims, info);
1401         info->win = window;
1402         info->locale = strdup(locale);
1403         info->reconnecting = EINA_FALSE;
1404      }
1405
1406    xim_info_try_im(info);
1407    return info;
1408 }
1409
1410 /* initialize info->im */
1411 static void
1412 xim_info_try_im(XIM_Im_Info *info)
1413 {
1414    Ecore_X_Display *dsp;
1415
1416    assert(info->im == NULL);
1417    if (info->reconnecting == EINA_TRUE)
1418      return;
1419
1420    if (XSupportsLocale())
1421      {
1422         if (!XSetLocaleModifiers(""))
1423           EINA_LOG_WARN("Unable to set locale modifiers with XSetLocaleModifiers()");
1424         dsp = ecore_x_display_get();
1425         info->im = XOpenIM(dsp, NULL, NULL, NULL);
1426         if (!info->im)
1427           {
1428              XRegisterIMInstantiateCallback(dsp,
1429                                             NULL, NULL, NULL,
1430                                             xim_instantiate_callback,
1431                                             (XPointer)info);
1432              info->reconnecting = EINA_TRUE;
1433              return;
1434           }
1435         setup_im(info);
1436      }
1437 }
1438
1439 static void
1440 xim_info_display_closed(Ecore_X_Display *display __UNUSED__,
1441                         int is_error __UNUSED__,
1442                         XIM_Im_Info *info)
1443 {
1444    Eina_List *ics, *tmp_list;
1445    Ecore_IMF_Context *ctx;
1446
1447    open_ims = eina_list_remove(open_ims, info);
1448
1449    ics = info->ics;
1450    info->ics = NULL;
1451
1452    EINA_LIST_FOREACH (ics, tmp_list, ctx)
1453      set_ic_client_window(ctx, 0);
1454
1455    EINA_LIST_FREE (ics, ctx)
1456      {
1457         Ecore_IMF_Context_Data *imf_context_data;
1458         imf_context_data = ecore_imf_context_data_get(ctx);
1459         imf_context_data_destroy(imf_context_data);
1460      }
1461
1462    free(info->locale);
1463
1464    if (info->im)
1465      XCloseIM(info->im);
1466
1467    free(info);
1468 }
1469
1470 static void
1471 xim_instantiate_callback(Display *display,
1472                          XPointer client_data,
1473                          XPointer call_data __UNUSED__)
1474 {
1475    XIM_Im_Info *info = (XIM_Im_Info *)client_data;
1476    XIM im = NULL;
1477
1478    im = XOpenIM(display, NULL, NULL, NULL);
1479
1480    if (!im)
1481      {
1482         fprintf(stderr, "Failed to connect to IM\n");
1483         return;
1484      }
1485
1486    info->im = im;
1487    setup_im(info);
1488
1489    XUnregisterIMInstantiateCallback(display, NULL, NULL, NULL,
1490                                     xim_instantiate_callback,
1491                                     (XPointer)info);
1492    info->reconnecting = EINA_FALSE;
1493 }
1494
1495 static void
1496 setup_im(XIM_Im_Info *info)
1497 {
1498    XIMValuesList *ic_values = NULL;
1499    XIMCallback im_destroy_callback;
1500
1501    if (!info->im)
1502      return;
1503
1504    im_destroy_callback.client_data = (XPointer)info;
1505    im_destroy_callback.callback = (XIMProc)xim_destroy_callback;
1506    XSetIMValues(info->im,
1507                 XNDestroyCallback, &im_destroy_callback,
1508                 NULL);
1509
1510    XGetIMValues(info->im,
1511                 XNQueryInputStyle, &info->xim_styles,
1512                 XNQueryICValuesList, &ic_values,
1513                 NULL);
1514
1515    if (ic_values)
1516      {
1517         int i;
1518
1519         for (i = 0; i < ic_values->count_values; i++)
1520           {
1521              if (!strcmp(ic_values->supported_values[i],
1522                          XNStringConversionCallback))
1523                info->supports_string_conversion = EINA_TRUE;
1524              if (!strcmp(ic_values->supported_values[i],
1525                          XNCursor))
1526                info->supports_cursor = EINA_TRUE;
1527           }
1528 #if 0
1529         printf("values........\n");
1530         for (i = 0; i < ic_values->count_values; i++)
1531           printf("%s\n", ic_values->supported_values[i]);
1532         printf("styles........\n");
1533         for (i = 0; i < info->xim_styles->count_styles; i++)
1534           printf("%lx\n", info->xim_styles->supported_styles[i]);
1535 #endif
1536         XFree(ic_values);
1537      }
1538 }
1539
1540 static void
1541 xim_destroy_callback(XIM xim __UNUSED__,
1542                      XPointer client_data,
1543                      XPointer call_data __UNUSED__)
1544 {
1545    XIM_Im_Info *info = (XIM_Im_Info *)client_data;
1546
1547    if (info->user) info->user->ic = NULL;
1548    info->im = NULL;
1549 //   reinitialize_ic(ctx);
1550    xim_info_try_im(info);
1551
1552    return;
1553 }
1554
1555 #endif  /* ENABLE_XIM */