f5aabd032102a0c60fb55f155f177ee8513aa386
[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              XComposeStatus status;
742              val = XLookupString(&xev,
743                                  compose_buffer,
744                                  sizeof(compose_buffer),
745                                  &sym,
746                                  &status);
747              if (val > 0)
748                {
749                   compose_buffer[val] = '\0';
750                   compose = eina_str_convert(nl_langinfo(CODESET),
751                                              "UTF-8", compose_buffer);
752                }
753           }
754
755         if (compose)
756           {
757              Eina_Unicode *unicode;
758              int len;
759              unicode = eina_unicode_utf8_to_unicode(compose, &len);
760              if (!unicode) abort();
761              if (unicode[0] >= 0x20 && unicode[0] != 0x7f)
762                {
763                   ecore_imf_context_commit_event_add(ctx, compose);
764                   ecore_imf_context_event_callback_call(ctx, ECORE_IMF_CALLBACK_COMMIT, compose);
765                   result = EINA_TRUE;
766                }
767              free(compose);
768              free(unicode);
769           }
770      }
771
772    return result;
773 #else
774    (void)ctx;
775    (void)type;
776    (void)event;
777    return EINA_FALSE;
778 #endif
779 }
780
781 static const Ecore_IMF_Context_Info xim_info = {
782    .id = "xim",
783    .description = _("X input method"),
784    .default_locales = "ko:ja:th:zh",
785    .canvas_type = "evas",
786    .canvas_required = 1,
787 };
788
789 static Ecore_IMF_Context_Class xim_class = {
790    .add = _ecore_imf_context_xim_add,
791    .del = _ecore_imf_context_xim_del,
792    .client_window_set = _ecore_imf_context_xim_client_window_set,
793    .client_canvas_set = NULL,
794    .show = _ecore_imf_context_xim_input_panel_show,
795    .hide = _ecore_imf_context_xim_input_panel_hide,
796    .preedit_string_get = _ecore_imf_context_xim_preedit_string_get,
797    .focus_in = _ecore_imf_context_xim_focus_in,
798    .focus_out = _ecore_imf_context_xim_focus_out,
799    .reset = _ecore_imf_context_xim_reset,
800    .cursor_position_set = NULL,
801    .use_preedit_set = _ecore_imf_context_xim_use_preedit_set,
802    .input_mode_set = NULL,
803    .filter_event = _ecore_imf_context_xim_filter_event,
804    .preedit_string_with_attributes_get = _ecore_imf_context_xim_preedit_string_with_attributes_get,
805    .prediction_allow_set = NULL,
806    .autocapital_type_set = NULL,
807    .control_panel_show = NULL,
808    .control_panel_hide = NULL,
809    .input_panel_layout_set = NULL,
810    .input_panel_layout_get = NULL,
811    .input_panel_language_set = NULL,
812    .input_panel_language_get = NULL,
813    .cursor_location_set = _ecore_imf_context_xim_cursor_location_set,
814    .input_panel_imdata_set = NULL,
815    .input_panel_imdata_get = NULL,
816    .input_panel_return_key_type_set = NULL,
817    .input_panel_return_key_disabled_set = NULL,
818    .input_panel_caps_lock_mode_set = NULL
819 };
820
821 static Ecore_IMF_Context *
822 xim_imf_module_create(void)
823 {
824    EINA_LOG_DBG("%s in", __FUNCTION__);
825    Ecore_IMF_Context *ctx = NULL;
826
827    ctx = ecore_imf_context_new(&xim_class);
828    if (!ctx)
829      goto error;
830
831    return ctx;
832
833 error:
834    free(ctx);
835    return NULL;
836 }
837
838 static Ecore_IMF_Context *
839 xim_imf_module_exit(void)
840 {
841    return NULL;
842 }
843
844 Eina_Bool
845 ecore_imf_xim_init(void)
846 {
847    EINA_LOG_DBG("%s in", __FUNCTION__);
848    eina_init();
849    ecore_x_init(NULL);
850    ecore_imf_module_register(&xim_info,
851                              xim_imf_module_create,
852                              xim_imf_module_exit);
853
854    return EINA_TRUE;
855 }
856
857 void
858 ecore_imf_xim_shutdown(void)
859 {
860 #ifdef ENABLE_XIM
861    while (open_ims)
862      {
863         XIM_Im_Info *info = open_ims->data;
864         Ecore_X_Display *display = ecore_x_display_get();
865
866         xim_info_display_closed(display, EINA_FALSE, info);
867      }
868 #endif
869
870    ecore_x_shutdown();
871    eina_shutdown();
872 }
873
874 EINA_MODULE_INIT(ecore_imf_xim_init);
875 EINA_MODULE_SHUTDOWN(ecore_imf_xim_shutdown);
876
877 #ifdef ENABLE_XIM
878 /*
879  * internal functions
880  */
881 Ecore_IMF_Context_Data *
882 imf_context_data_new()
883 {
884    Ecore_IMF_Context_Data *imf_context_data = NULL;
885    char *locale;
886
887    locale = setlocale(LC_CTYPE, "");
888    if (!locale) return NULL;
889
890    if (!XSupportsLocale()) return NULL;
891
892    imf_context_data = calloc(1, sizeof(Ecore_IMF_Context_Data));
893    EINA_SAFETY_ON_NULL_RETURN_VAL(imf_context_data, NULL);
894
895    imf_context_data->locale = strdup(locale);
896    if (!imf_context_data->locale) goto error;
897
898    return imf_context_data;
899 error:
900    imf_context_data_destroy(imf_context_data);
901    return NULL;
902 }
903
904 void
905 imf_context_data_destroy(Ecore_IMF_Context_Data *imf_context_data)
906 {
907    if (!imf_context_data)
908      return;
909
910    if (imf_context_data->ic)
911      XDestroyIC(imf_context_data->ic);
912
913    free(imf_context_data->preedit_chars);
914
915    if (imf_context_data->feedbacks)
916      {
917         free(imf_context_data->feedbacks);
918         imf_context_data->feedbacks = NULL;
919      }
920
921    free(imf_context_data->locale);
922    free(imf_context_data);
923 }
924
925 static int
926 preedit_start_callback(XIC xic __UNUSED__,
927                        XPointer client_data,
928                        XPointer call_data __UNUSED__)
929 {
930    EINA_LOG_DBG("in");
931    Ecore_IMF_Context *ctx = (Ecore_IMF_Context *)client_data;
932    Ecore_IMF_Context_Data *imf_context_data;
933    imf_context_data = ecore_imf_context_data_get(ctx);
934    if (!imf_context_data) return -1;
935
936    if (imf_context_data->finalizing == EINA_FALSE)
937      {
938         ecore_imf_context_preedit_start_event_add(ctx);
939         ecore_imf_context_event_callback_call(ctx, ECORE_IMF_CALLBACK_PREEDIT_START, NULL);
940      }
941    return -1;
942 }
943
944 static void
945 preedit_done_callback(XIC xic __UNUSED__,
946                       XPointer client_data,
947                       XPointer call_data __UNUSED__)
948 {
949    EINA_LOG_DBG("in");
950    Ecore_IMF_Context *ctx = (Ecore_IMF_Context *)client_data;
951    Ecore_IMF_Context_Data *imf_context_data;
952    imf_context_data = ecore_imf_context_data_get(ctx);
953    EINA_SAFETY_ON_NULL_RETURN(imf_context_data);
954
955    if (imf_context_data->preedit_length)
956      {
957         imf_context_data->preedit_length = 0;
958         free(imf_context_data->preedit_chars);
959         imf_context_data->preedit_chars = NULL;
960         ecore_imf_context_preedit_changed_event_add(ctx);
961         ecore_imf_context_event_callback_call(ctx, ECORE_IMF_CALLBACK_PREEDIT_CHANGED, NULL);
962      }
963
964    if (imf_context_data->finalizing == EINA_FALSE)
965      {
966         ecore_imf_context_preedit_end_event_add(ctx);
967         ecore_imf_context_event_callback_call(ctx, ECORE_IMF_CALLBACK_PREEDIT_END, NULL);
968      }
969 }
970
971 /* FIXME */
972 static int
973 xim_text_to_utf8(Ecore_IMF_Context *ctx __UNUSED__,
974                  XIMText *xim_text,
975                  char **text)
976 {
977    int text_length = 0;
978    char *result = NULL;
979
980    if (xim_text && xim_text->string.multi_byte)
981      {
982         if (xim_text->encoding_is_wchar)
983           {
984              EINA_LOG_WARN("Wide character return from Xlib not currently supported");
985              *text = NULL;
986              return 0;
987           }
988
989         /* XXX Convert to UTF-8 */
990         result = strdup(xim_text->string.multi_byte);
991         if (result)
992           {
993              text_length = eina_unicode_utf8_get_len(result);
994              if (text_length != xim_text->length)
995                {
996                   EINA_LOG_WARN("Size mismatch when converting text from input method: supplied length = %d\n, result length = %d", xim_text->length, text_length);
997                }
998           }
999         else
1000           {
1001              EINA_LOG_WARN("Error converting text from IM to UCS-4");
1002              *text = NULL;
1003              return 0;
1004           }
1005
1006         *text = result;
1007         return text_length;
1008      }
1009    else
1010      {
1011         *text = NULL;
1012         return 0;
1013      }
1014 }
1015
1016 static void
1017 preedit_draw_callback(XIC xic __UNUSED__,
1018                       XPointer client_data,
1019                       XIMPreeditDrawCallbackStruct *call_data)
1020 {
1021    EINA_LOG_DBG("in");
1022    Eina_Bool ret = EINA_FALSE;
1023    Ecore_IMF_Context *ctx = (Ecore_IMF_Context *)client_data;
1024    Ecore_IMF_Context_Data *imf_context_data = ecore_imf_context_data_get(ctx);
1025    XIMText *t = call_data->text;
1026    char *tmp;
1027    Eina_Unicode *new_text = NULL;
1028    Eina_UStrbuf *preedit_bufs = NULL;
1029    int new_text_length;
1030    int i = 0;
1031
1032    EINA_SAFETY_ON_NULL_RETURN(imf_context_data);
1033
1034    preedit_bufs = eina_ustrbuf_new();
1035    if (imf_context_data->preedit_chars)
1036      {
1037         ret = eina_ustrbuf_append(preedit_bufs, imf_context_data->preedit_chars);
1038         if (ret == EINA_FALSE) goto done;
1039      }
1040
1041    new_text_length = xim_text_to_utf8(ctx, t, &tmp);
1042    if (tmp)
1043      {
1044         int tmp_len;
1045         new_text = eina_unicode_utf8_to_unicode((const char *)tmp, &tmp_len);
1046         free(tmp);
1047      }
1048
1049    if (t == NULL)
1050      {
1051         /* delete string */
1052         ret = eina_ustrbuf_remove(preedit_bufs,
1053                                   call_data->chg_first, call_data->chg_length);
1054      }
1055    else if (call_data->chg_length == 0)
1056      {
1057         /* insert string */
1058         ret = eina_ustrbuf_insert(preedit_bufs, new_text, call_data->chg_first);
1059      }
1060    else if (call_data->chg_length > 0)
1061      {
1062         /* replace string */
1063         ret = eina_ustrbuf_remove(preedit_bufs,
1064                                   call_data->chg_first, call_data->chg_length);
1065         if (ret == EINA_FALSE) goto done;
1066
1067         ret = eina_ustrbuf_insert_n(preedit_bufs, new_text,
1068                                     new_text_length, call_data->chg_first);
1069         if (ret == EINA_FALSE) goto done;
1070      }
1071    else
1072      {
1073         ret = EINA_FALSE;
1074      }
1075
1076 done:
1077    if (ret == EINA_TRUE)
1078      {
1079         free(imf_context_data->preedit_chars);
1080         imf_context_data->preedit_chars =
1081           eina_ustrbuf_string_steal(preedit_bufs);
1082         imf_context_data->preedit_length =
1083           eina_unicode_strlen(imf_context_data->preedit_chars);
1084
1085         if (imf_context_data->feedbacks)
1086           {
1087              free(imf_context_data->feedbacks);
1088              imf_context_data->feedbacks = NULL;
1089           }
1090
1091         if (imf_context_data->preedit_length > 0)
1092           {
1093              imf_context_data->feedbacks = calloc(imf_context_data->preedit_length, sizeof(XIMFeedback));
1094
1095              for (i = 0; i < imf_context_data->preedit_length; i++)
1096                imf_context_data->feedbacks[i] = t->feedback[i];
1097           }
1098
1099         ecore_imf_context_preedit_changed_event_add(ctx);
1100         ecore_imf_context_event_callback_call(ctx, ECORE_IMF_CALLBACK_PREEDIT_CHANGED, NULL);
1101      }
1102
1103    free(new_text);
1104    eina_ustrbuf_free(preedit_bufs);
1105 }
1106
1107 static void
1108 preedit_caret_callback(XIC xic __UNUSED__,
1109                        XPointer client_data,
1110                        XIMPreeditCaretCallbackStruct *call_data)
1111 {
1112    EINA_LOG_DBG("in");
1113    Ecore_IMF_Context *ctx = (Ecore_IMF_Context *)client_data;
1114    Ecore_IMF_Context_Data *imf_context_data;
1115    imf_context_data = ecore_imf_context_data_get(ctx);
1116    EINA_SAFETY_ON_NULL_RETURN(imf_context_data);
1117
1118    if (call_data->direction == XIMAbsolutePosition)
1119      {
1120         // printf("call_data->position:%d\n", call_data->position);
1121         imf_context_data->preedit_cursor = call_data->position;
1122         if (imf_context_data->finalizing == EINA_FALSE)
1123           {
1124              ecore_imf_context_preedit_changed_event_add(ctx);
1125              ecore_imf_context_event_callback_call(ctx, ECORE_IMF_CALLBACK_PREEDIT_CHANGED, NULL);
1126           }
1127      }
1128 }
1129
1130 static XVaNestedList
1131 preedit_callback_set(Ecore_IMF_Context *ctx)
1132 {
1133    Ecore_IMF_Context_Data *imf_context_data;
1134    imf_context_data = ecore_imf_context_data_get(ctx);
1135
1136    imf_context_data->preedit_start_cb.client_data = (XPointer)ctx;
1137    imf_context_data->preedit_start_cb.callback = (XIMProc)preedit_start_callback;
1138
1139    imf_context_data->preedit_done_cb.client_data = (XPointer)ctx;
1140    imf_context_data->preedit_done_cb.callback = (XIMProc)preedit_done_callback;
1141
1142    imf_context_data->preedit_draw_cb.client_data = (XPointer)ctx;
1143    imf_context_data->preedit_draw_cb.callback = (XIMProc)preedit_draw_callback;
1144
1145    imf_context_data->preedit_caret_cb.client_data = (XPointer)ctx;
1146    imf_context_data->preedit_caret_cb.callback = (XIMProc)preedit_caret_callback;
1147
1148    return XVaCreateNestedList(0,
1149                               XNPreeditStartCallback,
1150                               &imf_context_data->preedit_start_cb,
1151                               XNPreeditDoneCallback,
1152                               &imf_context_data->preedit_done_cb,
1153                               XNPreeditDrawCallback,
1154                               &imf_context_data->preedit_draw_cb,
1155                               XNPreeditCaretCallback,
1156                               &imf_context_data->preedit_caret_cb,
1157                               NULL);
1158 }
1159
1160 static XIC
1161 get_ic(Ecore_IMF_Context *ctx)
1162 {
1163    Ecore_IMF_Context_Data *imf_context_data;
1164    XIC ic;
1165    imf_context_data = ecore_imf_context_data_get(ctx);
1166    EINA_SAFETY_ON_NULL_RETURN_VAL(imf_context_data, NULL);
1167
1168    ic = imf_context_data->ic;
1169    if (!ic)
1170      {
1171         XIM_Im_Info *im_info = imf_context_data->im_info;
1172         XVaNestedList preedit_attr = NULL;
1173         XIMStyle im_style = 0;
1174         XPoint spot = { 0, 0 };
1175         char *name = NULL;
1176
1177         if (!im_info)
1178           {
1179              EINA_LOG_WARN("Doesn't open XIM.");
1180              return NULL;
1181           }
1182
1183         // supported styles
1184 #if 0
1185         int i;
1186         if (im_info->xim_styles)
1187           {
1188              for (i = 0; i < im_info->xim_styles->count_styles; i++)
1189                {
1190                   printf("%i: ", i);
1191                   if (im_info->xim_styles->supported_styles[i] & XIMPreeditCallbacks)
1192                     printf("XIMPreeditCallbacks | ");
1193                   if (im_info->xim_styles->supported_styles[i] & XIMPreeditPosition)
1194                     printf("XIMPreeditPosition | ");
1195                   if (im_info->xim_styles->supported_styles[i] & XIMPreeditArea)
1196                     printf("XIMPreeditArea | ");
1197                   if (im_info->xim_styles->supported_styles[i] & XIMPreeditNothing)
1198                     printf("XIMPreeditNothing | ");
1199                   if (im_info->xim_styles->supported_styles[i] & XIMPreeditNone)
1200                     printf("XIMPreeditNone | ");
1201                   if (im_info->xim_styles->supported_styles[i] & XIMStatusArea)
1202                     printf("XIMStatusArea | ");
1203                   if (im_info->xim_styles->supported_styles[i] & XIMStatusCallbacks)
1204                     printf("XIMStatusCallbacks | ");
1205                   if (im_info->xim_styles->supported_styles[i] & XIMStatusNothing)
1206                     printf("XIMStatusNothing | ");
1207                   if (im_info->xim_styles->supported_styles[i] & XIMStatusNone)
1208                     printf("XIMStatusNone | ");
1209                   printf("\n");
1210                }
1211           }
1212 #endif
1213         // "OverTheSpot" = XIMPreeditPosition | XIMStatusNothing
1214         // "OffTheSpot" = XIMPreeditArea | XIMStatusArea
1215         // "Root" = XIMPreeditNothing | XIMStatusNothing
1216
1217         if (imf_context_data->use_preedit == EINA_TRUE)
1218           {
1219              if (im_info->supports_cursor)
1220                {
1221                   // kinput2 DOES do this...
1222                   XFontSet fs;
1223                   char **missing_charset_list;
1224                   int missing_charset_count;
1225                   char *def_string;
1226
1227                   im_style |= XIMPreeditPosition;
1228                   im_style |= XIMStatusNothing;
1229                   fs = XCreateFontSet(ecore_x_display_get(),
1230                                       "fixed",
1231                                       &missing_charset_list,
1232                                       &missing_charset_count,
1233                                       &def_string);
1234                   preedit_attr = XVaCreateNestedList(0,
1235                                                      XNSpotLocation, &spot,
1236                                                      XNFontSet, fs,
1237                                                      NULL);
1238                }
1239              else
1240                {
1241                   im_style |= XIMPreeditCallbacks;
1242                   im_style |= XIMStatusNothing;
1243                   preedit_attr = preedit_callback_set(ctx);
1244                }
1245              name = XNPreeditAttributes;
1246           }
1247         else
1248           {
1249              im_style |= XIMPreeditNothing;
1250              im_style |= XIMStatusNothing;
1251           }
1252
1253         if (!im_info->xim_styles)
1254           {
1255              EINA_LOG_WARN("No XIM styles supported! Wanted %#llx",
1256                            (unsigned long long)im_style);
1257              im_style = 0;
1258           }
1259         else
1260           {
1261              XIMStyle fallback = 0;
1262              int i;
1263
1264              for (i = 0; i < im_info->xim_styles->count_styles; i++)
1265                {
1266                   XIMStyle cur = im_info->xim_styles->supported_styles[i];
1267                   if (cur == im_style)
1268                     break;
1269                   else if (cur == (XIMPreeditNothing | XIMStatusNothing))
1270                     /* TODO: fallback is just that or the anyone? */
1271                     fallback = cur;
1272                }
1273
1274              if (i == im_info->xim_styles->count_styles)
1275                {
1276                   if (fallback)
1277                     {
1278                        EINA_LOG_WARN("Wanted XIM style %#llx not found, "
1279                                      "using fallback %#llx instead.",
1280                                      (unsigned long long)im_style,
1281                                      (unsigned long long)fallback);
1282                        im_style = fallback;
1283                     }
1284                   else
1285                     {
1286                        EINA_LOG_WARN("Wanted XIM style %#llx not found, "
1287                                      "no fallback supported.",
1288                                      (unsigned long long)im_style);
1289                        im_style = 0;
1290                     }
1291                }
1292           }
1293
1294         if ((im_info->im) && (im_style))
1295           {
1296              ic = XCreateIC(im_info->im,
1297                             XNInputStyle, im_style,
1298                             XNClientWindow, imf_context_data->win,
1299                             name, preedit_attr, NULL);
1300           }
1301         XFree(preedit_attr);
1302         if (ic)
1303           {
1304              unsigned long mask = 0xaaaaaaaa;
1305              XGetICValues(ic,
1306                           XNFilterEvents, &mask,
1307                           NULL);
1308              imf_context_data->mask = mask;
1309              ecore_x_event_mask_set(imf_context_data->win, mask);
1310           }
1311
1312         imf_context_data->ic = ic;
1313         if (ic && imf_context_data->has_focus == EINA_TRUE)
1314           XSetICFocus(ic);
1315      }
1316
1317    return ic;
1318 }
1319
1320 static void
1321 reinitialize_ic(Ecore_IMF_Context *ctx)
1322 {
1323    Ecore_IMF_Context_Data *imf_context_data = ecore_imf_context_data_get(ctx);
1324    EINA_SAFETY_ON_NULL_RETURN(imf_context_data);
1325
1326    XIC ic = imf_context_data->ic;
1327    if (ic)
1328      {
1329         XDestroyIC(ic);
1330         imf_context_data->ic = NULL;
1331         if (imf_context_data->preedit_length)
1332           {
1333              imf_context_data->preedit_length = 0;
1334              free(imf_context_data->preedit_chars);
1335              imf_context_data->preedit_chars = NULL;
1336              ecore_imf_context_preedit_changed_event_add(ctx);
1337              ecore_imf_context_event_callback_call(ctx, ECORE_IMF_CALLBACK_PREEDIT_CHANGED, NULL);
1338           }
1339      }
1340 }
1341
1342 static void
1343 set_ic_client_window(Ecore_IMF_Context *ctx,
1344                      Ecore_X_Window window)
1345 {
1346    EINA_LOG_DBG("in");
1347    Ecore_IMF_Context_Data *imf_context_data = ecore_imf_context_data_get(ctx);
1348    Ecore_X_Window old_win;
1349
1350    EINA_SAFETY_ON_NULL_RETURN(imf_context_data);
1351
1352    /* reinitialize IC */
1353    reinitialize_ic(ctx);
1354
1355    old_win = imf_context_data->win;
1356    EINA_LOG_DBG("old_win:%d window:%d ", old_win, window);
1357    if (old_win != 0 && old_win != window)   /* XXX how do check window... */
1358      {
1359         XIM_Im_Info *info;
1360         info = imf_context_data->im_info;
1361         info->ics = eina_list_remove(info->ics, imf_context_data);
1362         if (imf_context_data->im_info)
1363           imf_context_data->im_info->user = NULL;
1364         imf_context_data->im_info = NULL;
1365      }
1366
1367    imf_context_data->win = window;
1368
1369    if (window) /* XXX */
1370      {
1371         XIM_Im_Info *info = NULL;
1372         info = get_im(window, imf_context_data->locale);
1373         imf_context_data->im_info = info;
1374         imf_context_data->im_info->ics =
1375           eina_list_prepend(imf_context_data->im_info->ics,
1376                             imf_context_data);
1377         if (imf_context_data->im_info)
1378           imf_context_data->im_info->user = imf_context_data;
1379      }
1380 }
1381
1382 static XIM_Im_Info *
1383 get_im(Ecore_X_Window window,
1384        char *locale)
1385 {
1386    EINA_LOG_DBG("in");
1387
1388    Eina_List *l;
1389    XIM_Im_Info *im_info = NULL;
1390    XIM_Im_Info *info = NULL;
1391    EINA_LIST_FOREACH (open_ims, l, im_info)
1392      {
1393         if (strcmp(im_info->locale, locale) == 0)
1394           {
1395              if (im_info->im)
1396                {
1397                   return im_info;
1398                }
1399              else
1400                {
1401                   info = im_info;
1402                   break;
1403                }
1404           }
1405      }
1406
1407    if (!info)
1408      {
1409         info = calloc(1, sizeof(XIM_Im_Info));
1410         if (!info) return NULL;
1411         open_ims = eina_list_prepend(open_ims, info);
1412         info->win = window;
1413         info->locale = strdup(locale);
1414         info->reconnecting = EINA_FALSE;
1415      }
1416
1417    xim_info_try_im(info);
1418    return info;
1419 }
1420
1421 /* initialize info->im */
1422 static void
1423 xim_info_try_im(XIM_Im_Info *info)
1424 {
1425    Ecore_X_Display *dsp;
1426
1427    assert(info->im == NULL);
1428    if (info->reconnecting == EINA_TRUE)
1429      return;
1430
1431    if (XSupportsLocale())
1432      {
1433         if (!XSetLocaleModifiers(""))
1434           EINA_LOG_WARN("Unable to set locale modifiers with XSetLocaleModifiers()");
1435         dsp = ecore_x_display_get();
1436         info->im = XOpenIM(dsp, NULL, NULL, NULL);
1437         if (!info->im)
1438           {
1439              XRegisterIMInstantiateCallback(dsp,
1440                                             NULL, NULL, NULL,
1441                                             xim_instantiate_callback,
1442                                             (XPointer)info);
1443              info->reconnecting = EINA_TRUE;
1444              return;
1445           }
1446         setup_im(info);
1447      }
1448 }
1449
1450 static void
1451 xim_info_display_closed(Ecore_X_Display *display __UNUSED__,
1452                         int is_error __UNUSED__,
1453                         XIM_Im_Info *info)
1454 {
1455    Eina_List *ics, *tmp_list;
1456    Ecore_IMF_Context *ctx;
1457
1458    open_ims = eina_list_remove(open_ims, info);
1459
1460    ics = info->ics;
1461    info->ics = NULL;
1462
1463    EINA_LIST_FOREACH (ics, tmp_list, ctx)
1464      set_ic_client_window(ctx, 0);
1465
1466    EINA_LIST_FREE (ics, ctx)
1467      {
1468         Ecore_IMF_Context_Data *imf_context_data;
1469         imf_context_data = ecore_imf_context_data_get(ctx);
1470         imf_context_data_destroy(imf_context_data);
1471      }
1472
1473    free(info->locale);
1474
1475    if (info->im)
1476      XCloseIM(info->im);
1477
1478    free(info);
1479 }
1480
1481 static void
1482 xim_instantiate_callback(Display *display,
1483                          XPointer client_data,
1484                          XPointer call_data __UNUSED__)
1485 {
1486    XIM_Im_Info *info = (XIM_Im_Info *)client_data;
1487    XIM im = NULL;
1488
1489    im = XOpenIM(display, NULL, NULL, NULL);
1490
1491    if (!im)
1492      {
1493         fprintf(stderr, "Failed to connect to IM\n");
1494         return;
1495      }
1496
1497    info->im = im;
1498    setup_im(info);
1499
1500    XUnregisterIMInstantiateCallback(display, NULL, NULL, NULL,
1501                                     xim_instantiate_callback,
1502                                     (XPointer)info);
1503    info->reconnecting = EINA_FALSE;
1504 }
1505
1506 static void
1507 setup_im(XIM_Im_Info *info)
1508 {
1509    XIMValuesList *ic_values = NULL;
1510    XIMCallback im_destroy_callback;
1511
1512    if (!info->im)
1513      return;
1514
1515    im_destroy_callback.client_data = (XPointer)info;
1516    im_destroy_callback.callback = (XIMProc)xim_destroy_callback;
1517    XSetIMValues(info->im,
1518                 XNDestroyCallback, &im_destroy_callback,
1519                 NULL);
1520
1521    XGetIMValues(info->im,
1522                 XNQueryInputStyle, &info->xim_styles,
1523                 XNQueryICValuesList, &ic_values,
1524                 NULL);
1525
1526    if (ic_values)
1527      {
1528         int i;
1529
1530         for (i = 0; i < ic_values->count_values; i++)
1531           {
1532              if (!strcmp(ic_values->supported_values[i],
1533                          XNStringConversionCallback))
1534                info->supports_string_conversion = EINA_TRUE;
1535              if (!strcmp(ic_values->supported_values[i],
1536                          XNCursor))
1537                info->supports_cursor = EINA_TRUE;
1538           }
1539 #if 0
1540         printf("values........\n");
1541         for (i = 0; i < ic_values->count_values; i++)
1542           printf("%s\n", ic_values->supported_values[i]);
1543         printf("styles........\n");
1544         for (i = 0; i < info->xim_styles->count_styles; i++)
1545           printf("%lx\n", info->xim_styles->supported_styles[i]);
1546 #endif
1547         XFree(ic_values);
1548      }
1549 }
1550
1551 static void
1552 xim_destroy_callback(XIM xim __UNUSED__,
1553                      XPointer client_data,
1554                      XPointer call_data __UNUSED__)
1555 {
1556    XIM_Im_Info *info = (XIM_Im_Info *)client_data;
1557
1558    if (info->user) info->user->ic = NULL;
1559    info->im = NULL;
1560 //   reinitialize_ic(ctx);
1561    xim_info_try_im(info);
1562
1563    return;
1564 }
1565
1566 #endif  /* ENABLE_XIM */