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