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