1 /* -*- c-basic-offset:2; tab-width:2; indent-tabs-mode:nil -*- */
2
3 /*
4 * im_uim.c - uim plugin for mlterm
5 *
6 * Copyright (C) 2004 Seiichi SATO <ssato@sh.rim.or.jp>
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 * 1. Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in the
15 * documentation and/or other materials provided with the distribution.
16 * 3. The name of any author may not be used to endorse or promote
17 * products derived from this software without their specific prior
18 * written permission.
19 *
20 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
21 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
24 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30 * SUCH DAMAGE.
31 *
32 *
33 * $Id$
34 */
35
36 #include <stdio.h>
37 #include <uim.h>
38 #include <uim-helper.h>
39 #include <uim-im-switcher.h>
40
41 #if UIM_VERSION_REQUIRE(1, 5, 0)
42 #include <uim-scm.h>
43 #else
44 #define uim_scm_is_initialized() (initialized)
45 #endif
46
47 #include <pobl/bl_mem.h> /* malloc/alloca/free */
48 #include <pobl/bl_str.h> /* strdup/bl_str_sep/bl_snprintf */
49 #include <pobl/bl_locale.h> /* bl_get_locale */
50 #include <pobl/bl_slist.h>
51
52 #include <ui_im.h>
53
54 #include "../im_common.h"
55 #include "../im_info.h"
56
57 #if 0
58 #define IM_UIM_DEBUG 1
59 #endif
60
61 #if 0
62 /* Experimental (for twm, framebuffer etc where it's impossible to show status
63 * on system tray.) */
64 #define SHOW_STATUS_SCREEN
65 #endif
66
67 /*
68 * When uim encoding is the same as terminal, parser_uim and conv are NULL,
69 * so encoding of received string will not be converted.
70 */
71 #define NEED_TO_CONV(uim) ((uim)->parser_uim && (uim)->conv)
72
73 typedef struct im_uim {
74 /* input method common object */
75 ui_im_t im;
76
77 uim_context context;
78
79 vt_char_encoding_t term_encoding;
80
81 char *encoding_name; /* encoding of conversion engine */
82
83 /* parser_uim and conv are NULL if term_encoding == uim encoding */
84 ef_parser_t *parser_uim; /* for uim encoding */
85 ef_parser_t *parser_term; /* for term encoding */
86 ef_conv_t *conv; /* for term encoding */
87
88 u_int pressing_mod_key;
89 u_int mod_ignore_mask;
90
91 u_int cand_limit;
92
93 int is_mozc;
94
95 #ifdef SHOW_STATUS_SCREEN
96 int mode;
97 #endif
98
99 struct im_uim *next;
100
101 } im_uim_t;
102
103 /* --- static variables --- */
104
105 static im_uim_t *uim_list = NULL;
106 static int ref_count = 0;
107 static int initialized = 0;
108 static int helper_fd = -1;
109 static im_uim_t *focused_uim = NULL;
110 static ui_im_export_syms_t *syms = NULL; /* mlterm internal symbols */
111 static int mod_key_debug = 0;
112
113 /* --- static functions --- */
114
115 #ifdef SHOW_STATUS_SCREEN
update_stat_screen(im_uim_t * uim,int mode_changed)116 static void update_stat_screen(im_uim_t *uim, int mode_changed) {
117 if (!uim->im.cand_screen) {
118 if (uim->mode == 0 /* direct input */ /* || uim->im.preedit.filled_len == 0 */) {
119 if (uim->im.stat_screen) {
120 (*uim->im.stat_screen->destroy)(uim->im.stat_screen);
121 uim->im.stat_screen = NULL;
122 }
123
124 return;
125 }
126
127 if (!uim->im.stat_screen) {
128 int x;
129 int y;
130
131 (*uim->im.listener->get_spot)(uim->im.listener->self, NULL, 0, &x, &y);
132
133 if (!(uim->im.stat_screen = (*syms->ui_im_status_screen_new)(
134 uim->im.disp, uim->im.font_man, uim->im.color_man, uim->im.vtparser,
135 (*uim->im.listener->is_vertical)(uim->im.listener->self),
136 (*uim->im.listener->get_line_height)(uim->im.listener->self), x, y))) {
137 return;
138 }
139 } else if (!mode_changed) {
140 return;
141 }
142
143 (*uim->im.stat_screen->set)(uim->im.stat_screen, uim->parser_uim,
144 (u_char *)uim_get_mode_name(uim->context, uim->mode));
145 }
146 }
147 #else
148 #define update_stat_screen(uim, mode_changed) (0)
149 #endif
150
find_engine(const char * engine,char ** encoding_name)151 static int find_engine(const char *engine, char **encoding_name) {
152 uim_context u;
153 int i;
154 int result;
155
156 if (encoding_name == NULL) {
157 return 0;
158 }
159
160 if (!(u = uim_create_context(NULL, "UTF-8", NULL, NULL, NULL, NULL))) {
161 return 0;
162 }
163 result = 0;
164
165 for (i = 0; i < uim_get_nr_im(u); i++) {
166 if (strcmp(uim_get_im_name(u, i), engine) == 0) {
167 *encoding_name = (char *)uim_get_im_encoding(u, i);
168
169 if (*encoding_name && (*encoding_name = strdup(*encoding_name))) {
170 #ifdef IM_UIM_DEBUG
171 bl_debug_printf(BL_DEBUG_TAG "conversion engine: %s, native encoding: %s\n", engine,
172 *encoding_name);
173 #endif
174
175 result = 1;
176
177 break;
178 }
179 }
180 }
181
182 uim_release_context(u);
183
184 return result;
185 }
186
xksym_to_ukey(KeySym ksym)187 static int xksym_to_ukey(KeySym ksym) {
188 /* Latin 1 */
189 if (0x20 <= ksym && ksym <= 0x7e) {
190 return ksym;
191 }
192
193 switch (ksym) {
194 /* TTY Functions */
195 case XK_BackSpace:
196 return UKey_Backspace;
197 case XK_Tab:
198 return UKey_Tab;
199 case XK_Return:
200 return UKey_Return;
201 case XK_Escape:
202 return UKey_Escape;
203 case XK_Delete:
204 return UKey_Delete;
205 #ifdef XK_Multi_key
206 /* International & multi-key character composition */
207 case XK_Multi_key:
208 return UKey_Multi_key;
209 #endif
210 /* Japanese keyboard support */
211 case XK_Muhenkan:
212 return UKey_Muhenkan;
213 case XK_Henkan_Mode:
214 return UKey_Henkan_Mode;
215 case XK_Zenkaku_Hankaku:
216 return UKey_Zenkaku_Hankaku;
217 case XK_Hiragana_Katakana:
218 return UKey_Hiragana_Katakana;
219 /* Cursor control & motion */
220 case XK_Home:
221 return UKey_Home;
222 case XK_Left:
223 return UKey_Left;
224 case XK_Up:
225 return UKey_Up;
226 case XK_Right:
227 return UKey_Right;
228 case XK_Down:
229 return UKey_Down;
230 case XK_Prior:
231 return UKey_Prior;
232 case XK_Next:
233 return UKey_Next;
234 case XK_End:
235 return UKey_End;
236 case XK_F1:
237 return UKey_F1;
238 case XK_F2:
239 return UKey_F2;
240 case XK_F3:
241 return UKey_F3;
242 case XK_F4:
243 return UKey_F4;
244 case XK_F5:
245 return UKey_F5;
246 case XK_F6:
247 return UKey_F6;
248 case XK_F7:
249 return UKey_F7;
250 case XK_F8:
251 return UKey_F8;
252 case XK_F9:
253 return UKey_F9;
254 case XK_F10:
255 return UKey_F10;
256 case XK_F11:
257 return UKey_F11;
258 case XK_F12:
259 return UKey_F12;
260 case XK_F13:
261 return UKey_F13;
262 case XK_F14:
263 return UKey_F14;
264 case XK_F15:
265 return UKey_F15;
266 case XK_F16:
267 return UKey_F16;
268 case XK_F17:
269 return UKey_F17;
270 case XK_F18:
271 return UKey_F18;
272 case XK_F19:
273 return UKey_F19;
274 case XK_F20:
275 return UKey_F20;
276 case XK_F21:
277 return UKey_F21;
278 case XK_F22:
279 return UKey_F22;
280 case XK_F23:
281 return UKey_F23;
282 case XK_F24:
283 return UKey_F24;
284 #ifdef XK_F25
285 case XK_F25:
286 return UKey_F25;
287 case XK_F26:
288 return UKey_F26;
289 case XK_F27:
290 return UKey_F27;
291 case XK_F28:
292 return UKey_F28;
293 case XK_F29:
294 return UKey_F29;
295 case XK_F30:
296 return UKey_F30;
297 case XK_F31:
298 return UKey_F31;
299 case XK_F32:
300 return UKey_F32;
301 case XK_F33:
302 return UKey_F33;
303 case XK_F34:
304 return UKey_F34;
305 case XK_F35:
306 return UKey_F35;
307 #endif
308 #ifdef XK_KP_Space
309 case XK_KP_Space:
310 return ' ';
311 #endif
312 #ifdef XK_KP_Tab
313 case XK_KP_Tab:
314 return UKey_Tab;
315 #endif
316 #ifdef XK_KP_Enter
317 case XK_KP_Enter:
318 return UKey_Return;
319 #endif
320 case XK_KP_F1:
321 return UKey_F1;
322 case XK_KP_F2:
323 return UKey_F2;
324 case XK_KP_F3:
325 return UKey_F3;
326 case XK_KP_F4:
327 return UKey_F4;
328 case XK_KP_Home:
329 return UKey_Home;
330 case XK_KP_Left:
331 return UKey_Left;
332 case XK_KP_Up:
333 return UKey_Up;
334 case XK_KP_Right:
335 return UKey_Right;
336 case XK_KP_Down:
337 return UKey_Down;
338 case XK_KP_Prior:
339 return UKey_Prior;
340 case XK_KP_Next:
341 return UKey_Next;
342 case XK_KP_End:
343 return UKey_End;
344 case XK_KP_Delete:
345 return UKey_Delete;
346 #ifdef XK_KP_Equal
347 case XK_KP_Equal:
348 return '=';
349 #endif
350 case XK_KP_Multiply:
351 return '*';
352 case XK_KP_Add:
353 return '+';
354 case XK_KP_Separator:
355 return ',';
356 case XK_KP_Subtract:
357 return '-';
358 case XK_KP_Decimal:
359 return '.';
360 case XK_KP_Divide:
361 return '/';
362 /* keypad numbers */
363 case XK_KP_0:
364 return '0';
365 case XK_KP_1:
366 return '1';
367 case XK_KP_2:
368 return '2';
369 case XK_KP_3:
370 return '3';
371 case XK_KP_4:
372 return '4';
373 case XK_KP_5:
374 return '5';
375 case XK_KP_6:
376 return '6';
377 case XK_KP_7:
378 return '7';
379 case XK_KP_8:
380 return '8';
381 case XK_KP_9:
382 return '9';
383 default:
384 return UKey_Other;
385 }
386 }
387
helper_disconnected(void)388 static void helper_disconnected(void) {
389 (*syms->ui_event_source_remove_fd)(helper_fd);
390
391 helper_fd = -1;
392 }
393
prop_list_update(void * p,const char * str)394 static void prop_list_update(void *p, const char *str) {
395 im_uim_t *uim = NULL;
396 char buf[BUFSIZ];
397 int len;
398
399 #ifdef IM_UIM_DEBUG
400 bl_debug_printf(BL_DEBUG_TAG " str: %s\n", str);
401 #endif
402
403 uim = (im_uim_t *)p;
404
405 if (focused_uim != uim) {
406 return;
407 }
408
409 #define PROP_LIST_FORMAT "prop_list_update\ncharset=%s\n%s"
410
411 len = strlen(PROP_LIST_FORMAT) + strlen(uim->encoding_name) + strlen(str) + 1;
412
413 if (len > sizeof(buf)) {
414 #ifdef DEBUG
415 bl_warn_printf(BL_DEBUG_TAG " property list string is too long.");
416 #endif
417 return;
418 }
419
420 bl_snprintf(buf, sizeof(buf), PROP_LIST_FORMAT, uim->encoding_name, str);
421
422 uim_helper_send_message(helper_fd, buf);
423
424 #undef PROP_LIST_FORMAT
425 }
426
prop_label_update(void * p,const char * str)427 static void prop_label_update(void *p, const char *str) {
428 im_uim_t *uim = NULL;
429 char buf[BUFSIZ];
430 int len;
431
432 #ifdef IM_UIM_DEBUG
433 bl_debug_printf(BL_DEBUG_TAG " prop_label_update(), str: %s\n", str);
434 #endif
435
436 uim = (im_uim_t *)p;
437
438 if (focused_uim != uim) {
439 return;
440 }
441
442 #define PROP_LABEL_FORMAT "prop_label_update\ncharset=%s\n%s"
443
444 len = strlen(PROP_LABEL_FORMAT) + strlen(uim->encoding_name) + strlen(str) + 1;
445
446 if (len > sizeof(buf)) {
447 #ifdef DEBUG
448 bl_warn_printf(BL_DEBUG_TAG " property label string is too long.");
449 #endif
450
451 return;
452 }
453
454 bl_snprintf(buf, sizeof(buf), PROP_LABEL_FORMAT, uim->encoding_name, str);
455
456 uim_helper_send_message(helper_fd, buf);
457
458 #undef PROP_LABEL_FORMAT
459 }
460
461 #ifdef SHOW_STATUS_SCREEN
mode_update(void * p,int mode)462 static void mode_update(void *p, int mode) {
463 im_uim_t *uim = NULL;
464
465 uim = (im_uim_t *)p;
466
467 uim->mode = mode;
468
469 update_stat_screen(uim, 1);
470 }
471 #endif
472
commit(void * p,const char * str)473 static void commit(void *p, const char *str) {
474 im_uim_t *uim;
475
476 #ifdef IM_UIM_DEBUG
477 bl_debug_printf(BL_DEBUG_TAG "str: %s\n", str);
478 #endif
479
480 uim = (im_uim_t *)p;
481
482 (*uim->im.listener->write_to_term)(uim->im.listener->self, str, strlen(str),
483 uim->parser_uim);
484 }
485
preedit_clear(void * ptr)486 static void preedit_clear(void *ptr) {
487 im_uim_t *uim;
488
489 #ifdef IM_UIM_DEBUG
490 bl_debug_printf(BL_DEBUG_TAG "\n");
491 #endif
492
493 uim = (im_uim_t *)ptr;
494
495 if (uim->im.preedit.chars) {
496 (*syms->vt_str_destroy)(uim->im.preedit.chars, uim->im.preedit.num_chars);
497 uim->im.preedit.chars = NULL;
498 }
499
500 uim->im.preedit.num_chars = 0;
501 uim->im.preedit.filled_len = 0;
502 uim->im.preedit.segment_offset = 0;
503 uim->im.preedit.cursor_offset = UI_IM_PREEDIT_NOCURSOR;
504
505 update_stat_screen(uim, 0);
506 }
507
preedit_pushback(void * ptr,int attr,const char * _str)508 static void preedit_pushback(void *ptr, int attr, const char *_str) {
509 im_uim_t *uim;
510 u_char *str;
511 vt_char_t *p;
512 ef_char_t ch;
513 vt_color_t fg_color = VT_FG_COLOR;
514 vt_color_t bg_color = VT_BG_COLOR;
515 int is_underline = 0;
516 u_int count = 0;
517 size_t len;
518
519 #ifdef IM_UIM_DEBUG
520 bl_debug_printf(BL_DEBUG_TAG " attr: %d, _str:%s, length:%d\n", attr, _str, strlen(_str));
521 #endif
522
523 uim = (im_uim_t *)ptr;
524
525 if (attr & UPreeditAttr_Cursor) {
526 uim->im.preedit.cursor_offset = uim->im.preedit.filled_len;
527 }
528
529 if (!(len = strlen(_str))) {
530 return;
531 }
532
533 if (attr & UPreeditAttr_Reverse) {
534 uim->im.preedit.segment_offset = uim->im.preedit.filled_len;
535 uim->im.preedit.cursor_offset = UI_IM_PREEDIT_NOCURSOR;
536 fg_color = VT_BG_COLOR;
537 bg_color = VT_FG_COLOR;
538 }
539
540 if (attr & UPreeditAttr_UnderLine) {
541 is_underline = 1;
542 }
543
544 /* TODO: UPreeditAttr_Separator */
545
546 if (NEED_TO_CONV(uim)) {
547 /* uim encoding -> term encoding */
548 if (!im_convert_encoding(uim->parser_uim, uim->conv, (u_char *)_str, &str, len + 1)) {
549 return;
550 }
551 } else {
552 str = (u_char *)_str;
553 }
554
555 len = strlen(str);
556
557 /*
558 * count number of characters to re-allocate im.preedit.chars
559 */
560
561 (*uim->parser_term->init)(uim->parser_term);
562 (*uim->parser_term->set_str)(uim->parser_term, (u_char *)str, len);
563
564 while ((*uim->parser_term->next_char)(uim->parser_term, &ch)) {
565 count++;
566 }
567
568 /* no space left? (current array size < new array size?)*/
569 if (uim->im.preedit.num_chars < uim->im.preedit.filled_len + count) {
570 if (!(p = realloc(uim->im.preedit.chars,
571 sizeof(vt_char_t) * (uim->im.preedit.filled_len + count)))) {
572 #ifdef DEBUG
573 bl_warn_printf(BL_DEBUG_TAG " realloc failed.\n");
574 #endif
575
576 (*syms->vt_str_destroy)(uim->im.preedit.chars, uim->im.preedit.num_chars);
577 uim->im.preedit.chars = NULL;
578 uim->im.preedit.num_chars = 0;
579 uim->im.preedit.filled_len = 0;
580
581 if (NEED_TO_CONV(uim)) {
582 free(str);
583 }
584
585 return;
586 }
587
588 uim->im.preedit.chars = p;
589 uim->im.preedit.num_chars = uim->im.preedit.filled_len + count;
590 }
591
592 /*
593 * u_char --> vt_char_t
594 */
595
596 p = &uim->im.preedit.chars[uim->im.preedit.filled_len];
597 (*syms->vt_str_init)(p, count);
598
599 (*uim->parser_term->init)(uim->parser_term);
600 (*uim->parser_term->set_str)(uim->parser_term, (u_char *)str, len);
601
602 while ((*uim->parser_term->next_char)(uim->parser_term, &ch)) {
603 int is_fullwidth = 0;
604 int is_comb = 0;
605
606 if ((*syms->vt_convert_to_internal_ch)(uim->im.vtparser, &ch) <= 0) {
607 continue;
608 }
609
610 if (ch.property & EF_FULLWIDTH) {
611 is_fullwidth = 1;
612 } else if (ch.property & EF_AWIDTH) {
613 /* TODO: check col_size_of_width_a */
614 is_fullwidth = 1;
615 }
616
617 if (ch.property & EF_COMBINING) {
618 is_comb = 1;
619
620 if ((*syms->vt_char_combine)(p - 1, ef_char_to_int(&ch), ch.cs, is_fullwidth,
621 (ch.property & EF_AWIDTH) ? 1 : 0, is_comb,
622 fg_color, bg_color, 0, 0, is_underline, 0, 0)) {
623 continue;
624 }
625
626 /*
627 * if combining failed , char is normally appended.
628 */
629 }
630
631 (*syms->vt_char_set)(p, ef_char_to_int(&ch), ch.cs, is_fullwidth,
632 (ch.property & EF_AWIDTH) ? 1 : 0, is_comb, fg_color, bg_color,
633 0, 0, is_underline, 0, 0);
634
635 p++;
636 uim->im.preedit.filled_len++;
637 }
638
639 if (NEED_TO_CONV(uim)) {
640 free(str);
641 }
642 }
643
preedit_update(void * ptr)644 static void preedit_update(void *ptr) {
645 im_uim_t *uim;
646
647 uim = (im_uim_t *)ptr;
648
649 (*uim->im.listener->draw_preedit_str)(uim->im.listener->self, uim->im.preedit.chars,
650 uim->im.preedit.filled_len, uim->im.preedit.cursor_offset);
651 update_stat_screen(uim, 0);
652 }
653
654 /*
655 * callback for candidate screen events
656 */
657
candidate_selected(void * p,u_int index)658 static void candidate_selected(void *p, u_int index) {
659 #ifdef IM_UIM_DEBUG
660 bl_debug_printf(BL_DEBUG_TAG " index : %d\n", index);
661 #endif
662 uim_set_candidate_index(((im_uim_t *)p)->context, index);
663 }
664
665 /*
666 * callbacks for candidate selector
667 */
668
candidate_activate(void * p,int num,int limit)669 static void candidate_activate(void *p, int num, int limit) {
670 im_uim_t *uim;
671 int x;
672 int y;
673 int i;
674
675 #ifdef IM_UIM_DEBUG
676 bl_debug_printf(BL_DEBUG_TAG " num: %d limit: %d\n", num, limit);
677 #endif
678
679 uim = (im_uim_t *)p;
680
681 (*uim->im.listener->get_spot)(uim->im.listener->self, uim->im.preedit.chars,
682 uim->im.preedit.segment_offset, &x, &y);
683
684 if (uim->im.stat_screen) {
685 (*uim->im.stat_screen->destroy)(uim->im.stat_screen);
686 uim->im.stat_screen = NULL;
687 }
688
689 if (uim->im.cand_screen == NULL) {
690 if (!(uim->im.cand_screen = (*syms->ui_im_candidate_screen_new)(
691 uim->im.disp, uim->im.font_man, uim->im.color_man, uim->im.vtparser,
692 (*uim->im.listener->is_vertical)(uim->im.listener->self), 1,
693 (*uim->im.listener->get_line_height)(uim->im.listener->self), x, y))) {
694 #ifdef DEBUG
695 bl_warn_printf(BL_DEBUG_TAG " ui_im_candidate_screen_new() failed.\n");
696 #endif
697
698 return;
699 }
700
701 uim->im.cand_screen->listener.self = uim;
702 uim->im.cand_screen->listener.selected = candidate_selected;
703 }
704
705 if (!(*uim->im.cand_screen->init)(uim->im.cand_screen, num, limit)) {
706 (*uim->im.cand_screen->destroy)(uim->im.cand_screen);
707 uim->im.cand_screen = NULL;
708
709 return;
710 }
711
712 (*uim->im.cand_screen->set_spot)(uim->im.cand_screen, x, y);
713
714 for (i = 0; i < num; i++) {
715 uim_candidate c;
716 u_char *_p;
717 u_char *p = NULL;
718 const char *heading;
719 u_int info;
720
721 c = uim_get_candidate(uim->context, i, i);
722 _p = (u_char *)uim_candidate_get_cand_str(c);
723 heading = uim_candidate_get_heading_label(c);
724 if (heading && heading[0]) {
725 /* heading[1] may be '\0' */
726 info = (((u_int)heading[0]) << 16) | (((u_int)heading[1]) << 24) | i;
727 } else {
728 info = (((u_int)' ') << 16) | i;
729 }
730
731 #ifdef IM_UIM_DEBUG
732 bl_debug_printf(BL_DEBUG_TAG " %d%s%s%s| %s\n", i, (heading && heading[0]) ? "(" : "",
733 (heading && heading[0]) ? heading : "", (heading && heading[0]) ? ")" : "", _p);
734 #endif
735
736 if (NEED_TO_CONV(uim)) {
737 (*uim->parser_uim->init)(uim->parser_uim);
738 if (im_convert_encoding(uim->parser_uim, uim->conv, _p, &p, strlen(_p) + 1)) {
739 (*uim->im.cand_screen->set)(uim->im.cand_screen, uim->parser_term, p, info);
740 free(p);
741 }
742 } else {
743 (*uim->im.cand_screen->set)(uim->im.cand_screen, uim->parser_term, _p, i);
744 }
745
746 uim_candidate_free(c);
747 }
748
749 (*uim->im.cand_screen->select)(uim->im.cand_screen, 0);
750 uim->cand_limit = limit;
751 }
752
candidate_select(void * p,int index)753 static void candidate_select(void *p, int index) {
754 im_uim_t *uim;
755
756 #ifdef IM_UIM_DEBUG
757 bl_debug_printf(BL_DEBUG_TAG " index: %d\n", index);
758 #endif
759
760 uim = (im_uim_t *)p;
761
762 if (uim->im.cand_screen) {
763 /*
764 * XXX Hack for uim-mozc (1.11.1522.102)
765 * If candidate_activate() is called with num == 20 and limit = 10,
766 * uim_get_candidate() on mozc doesn't returns 20 candidates but 10 ones.
767 * (e.g. uim_get_candidate(0) and uim_get_candidate(10) returns the same.)
768 */
769 if (uim->is_mozc && uim->im.cand_screen->index != index &&
770 uim->im.cand_screen->index / uim->cand_limit != index / uim->cand_limit) {
771 int mod = index % uim->cand_limit;
772
773 if (mod == 0 || mod == uim->cand_limit - 1) {
774 candidate_activate(p, uim->im.cand_screen->num_candidates, uim->cand_limit);
775 }
776 }
777
778 (*uim->im.cand_screen->select)(uim->im.cand_screen, index);
779 }
780 }
781
candidate_shift_page(void * p,int direction)782 static void candidate_shift_page(void *p, int direction) {
783 im_uim_t *uim;
784 int index;
785
786 #ifdef IM_UIM_DEBUG
787 bl_debug_printf(BL_DEBUG_TAG " direction: %s\n", direction ? "next" : "prev");
788 #endif
789
790 uim = (im_uim_t *)p;
791
792 if (!uim->im.cand_screen) {
793 return;
794 }
795
796 index = (int)uim->im.cand_screen->index;
797
798 if (!direction && index < uim->cand_limit) {
799 /* top page -> last page */
800 index = (uim->im.cand_screen->num_candidates / uim->cand_limit) * uim->cand_limit + index;
801 } else if (direction &&
802 ((index / uim->cand_limit) + 1) * uim->cand_limit >
803 uim->im.cand_screen->num_candidates) {
804 /* last page -> top page */
805 index = index % uim->cand_limit;
806 } else {
807 /* shift page according to the direction */
808 index += (direction ? uim->cand_limit : -(uim->cand_limit));
809 }
810
811 if (index < 0) {
812 index = 0;
813 } else if (index >= uim->im.cand_screen->num_candidates) {
814 index = uim->im.cand_screen->num_candidates - 1;
815 }
816
817 (*uim->im.cand_screen->select)(uim->im.cand_screen, index);
818 uim_set_candidate_index(uim->context, index);
819 }
820
candidate_deactivate(void * p)821 static void candidate_deactivate(void *p) {
822 im_uim_t *uim;
823
824 #ifdef IM_UIM_DEBUG
825 bl_debug_printf(BL_DEBUG_TAG "\n");
826 #endif
827
828 uim = (im_uim_t *)p;
829
830 if (uim->im.cand_screen) {
831 (*uim->im.cand_screen->destroy)(uim->im.cand_screen);
832 uim->im.cand_screen = NULL;
833 }
834 }
835
836 /*
837 * methods of ui_im_t
838 */
839
destroy(ui_im_t * im)840 static void destroy(ui_im_t *im) {
841 im_uim_t *uim;
842
843 uim = (im_uim_t *)im;
844
845 if (focused_uim == uim) {
846 focused_uim = NULL;
847 }
848
849 if (uim->parser_uim) {
850 (*uim->parser_uim->destroy)(uim->parser_uim);
851 }
852
853 (*uim->parser_term->destroy)(uim->parser_term);
854
855 if (uim->conv) {
856 (*uim->conv->destroy)(uim->conv);
857 }
858
859 uim_release_context(uim->context);
860
861 ref_count--;
862
863 #ifdef IM_UIM_DEBUG
864 bl_debug_printf(BL_DEBUG_TAG " An object was destroyed. ref_count: %d\n", ref_count);
865 #endif
866
867 bl_slist_remove(uim_list, uim);
868
869 free(uim->encoding_name);
870 free(uim);
871
872 if (ref_count == 0 && initialized) {
873 (*syms->ui_event_source_remove_fd)(helper_fd);
874 uim_helper_close_client_fd(helper_fd);
875 helper_fd = -1;
876
877 if (initialized > 0) {
878 uim_quit();
879 initialized = 0;
880 }
881 }
882 }
883
key_event(ui_im_t * im,u_char key_char,KeySym ksym,XKeyEvent * event)884 static int key_event(ui_im_t *im, u_char key_char, KeySym ksym, XKeyEvent *event) {
885 im_uim_t *uim;
886 int key = 0;
887 int state = 0;
888 int ret;
889
890 int is_shift;
891 int is_lock;
892 int is_ctl;
893 int is_alt;
894 int is_meta;
895 int is_super;
896 int is_hyper;
897
898 uim = (im_uim_t *)im;
899
900 if (mod_key_debug) {
901 bl_msg_printf(">>--------------------------------\n");
902 bl_msg_printf(">>event->state : %.8x\n", event->state);
903 bl_msg_printf(">>mod_ignore_mask : %.8x\n", uim->mod_ignore_mask);
904 bl_msg_printf(">>ksym : %.8x\n", ksym);
905 }
906
907 #ifdef USE_XLIB
908 if (!(event->state & uim->mod_ignore_mask)) {
909 uim->pressing_mod_key = 0;
910 }
911
912 switch (ksym) {
913 case XK_Shift_L:
914 case XK_Shift_R:
915 uim->pressing_mod_key |= UMod_Shift;
916 break;
917 case XK_Control_L:
918 case XK_Control_R:
919 uim->pressing_mod_key |= UMod_Control;
920 break;
921 case XK_Alt_L:
922 case XK_Alt_R:
923 uim->pressing_mod_key |= UMod_Alt;
924 break;
925 case XK_Meta_L:
926 case XK_Meta_R:
927 uim->pressing_mod_key |= UMod_Meta;
928 break;
929 case XK_Super_L:
930 case XK_Super_R:
931 uim->pressing_mod_key |= UMod_Super;
932 break;
933 case XK_Hyper_L:
934 case XK_Hyper_R:
935 uim->pressing_mod_key |= UMod_Hyper;
936 break;
937 default:
938 break;
939 }
940 #else
941 uim->pressing_mod_key = ~0;
942 #endif
943
944 (*uim->im.listener->compare_key_state_with_modmap)(uim->im.listener->self, event->state,
945 &is_shift, &is_lock, &is_ctl, &is_alt,
946 &is_meta, NULL, &is_super, &is_hyper);
947
948 if (is_shift && (uim->pressing_mod_key & UMod_Shift)) {
949 state |= UMod_Shift;
950 }
951 if (is_ctl && (uim->pressing_mod_key & UMod_Control)) {
952 state |= UMod_Control;
953 }
954 if (is_alt && (uim->pressing_mod_key & UMod_Alt)) {
955 state |= UMod_Alt;
956 }
957 if (is_meta && (uim->pressing_mod_key & UMod_Meta)) {
958 state |= UMod_Meta;
959 }
960 if (is_super && (uim->pressing_mod_key & UMod_Super)) {
961 state |= UMod_Super;
962 }
963 if (is_hyper && (uim->pressing_mod_key & UMod_Hyper)) {
964 state |= UMod_Hyper;
965 }
966
967 if (mod_key_debug) {
968 bl_msg_printf(">>pressing_mod_key: %.8x\n", uim->pressing_mod_key);
969 bl_msg_printf(">>state : %.8x\n", state);
970 bl_msg_printf(">>--------------------------------\n");
971 }
972
973 key = xksym_to_ukey(ksym);
974
975 ret = uim_press_key(uim->context, key, state);
976 uim_release_key(uim->context, key, state);
977
978 return ret;
979 }
980
switch_mode(ui_im_t * im)981 static int switch_mode(ui_im_t *im) { return 0; }
982
is_active(ui_im_t * im)983 static int is_active(ui_im_t *im) {
984 #ifdef SHOW_STATUS_SCREEN
985 if (((im_uim_t *)im)->mode > 0) {
986 return 1;
987 } else
988 #endif
989 {
990 return 0;
991 }
992 }
993
focused(ui_im_t * im)994 static void focused(ui_im_t *im) {
995 im_uim_t *uim;
996
997 uim = (im_uim_t *)im;
998
999 uim_helper_client_focus_in(uim->context);
1000
1001 focused_uim = uim;
1002
1003 uim_prop_list_update(uim->context);
1004 uim_prop_label_update(uim->context);
1005
1006 if (uim->im.cand_screen) {
1007 (*uim->im.cand_screen->show)(uim->im.cand_screen);
1008 }
1009
1010 update_stat_screen(uim, 0);
1011
1012 uim->pressing_mod_key = 0;
1013 }
1014
unfocused(ui_im_t * im)1015 static void unfocused(ui_im_t *im) {
1016 im_uim_t *uim;
1017
1018 uim = (im_uim_t *)im;
1019
1020 uim_helper_client_focus_out(uim->context);
1021
1022 if (uim->im.cand_screen) {
1023 (*uim->im.cand_screen->hide)(uim->im.cand_screen);
1024 }
1025
1026 #ifdef SHOW_STATUS_SCREEN
1027 if (uim->im.stat_screen) {
1028 (*uim->im.stat_screen->hide)(uim->im.stat_screen);
1029 }
1030 #endif
1031
1032 uim->pressing_mod_key = 0;
1033 }
1034
1035 /*
1036 * helper
1037 */
1038
helper_send_imlist(void)1039 static void helper_send_imlist(void) {
1040 const char *selected_name;
1041 const char *name;
1042 const char *lang;
1043 const char *dsc;
1044 char *buf = NULL;
1045 int i;
1046 u_int len = 0;
1047 u_int filled_len = 0;
1048
1049 if (!focused_uim) {
1050 return;
1051 }
1052
1053 #define HEADER_FORMAT "im_list\ncharset=%s\n"
1054
1055 len += strlen(HEADER_FORMAT) + strlen(focused_uim->encoding_name);
1056
1057 selected_name = uim_get_current_im_name(focused_uim->context);
1058 len += strlen(selected_name);
1059 len += strlen("selected");
1060
1061 for (i = 0; i < uim_get_nr_im(focused_uim->context); i++) {
1062 name = uim_get_im_name(focused_uim->context, i);
1063 lang = uim_get_im_language(focused_uim->context, i);
1064 dsc = uim_get_im_short_desc(focused_uim->context, i);
1065
1066 len += name ? strlen(name) : 0;
1067 len += lang ? strlen(lang) : 0;
1068 len += dsc ? strlen(dsc) : 0;
1069 len += strlen("\t\t\t\n");
1070 }
1071
1072 len++;
1073
1074 if (!(buf = alloca(sizeof(char) * len))) {
1075 #ifdef DEBUG
1076 bl_warn_printf(BL_DEBUG_TAG " alloca failed\n");
1077 #endif
1078 return;
1079 }
1080
1081 filled_len = bl_snprintf(buf, len, HEADER_FORMAT, focused_uim->encoding_name);
1082
1083 #undef HEADER_FORMAT
1084
1085 for (i = 0; i < uim_get_nr_im(focused_uim->context); i++) {
1086 name = uim_get_im_name(focused_uim->context, i);
1087 lang = uim_get_im_language(focused_uim->context, i);
1088 dsc = uim_get_im_short_desc(focused_uim->context, i);
1089
1090 filled_len += bl_snprintf(&buf[filled_len], len - filled_len, "%s\t%s\t%s\t%s\n",
1091 name ? name : "", lang ? lang : "", dsc ? dsc : "",
1092 strcmp(name, selected_name) == 0 ? "selected" : "");
1093 }
1094
1095 #ifdef IM_UIM_DEBUG
1096 bl_debug_printf("----\n%s----\n", buf);
1097 #endif
1098
1099 uim_helper_send_message(helper_fd, buf);
1100 }
1101
helper_im_changed(char * request,char * engine_name)1102 static void helper_im_changed(char *request, char *engine_name) {
1103 char *buf;
1104 size_t len;
1105
1106 len = strlen(engine_name) + 5;
1107
1108 if (!(buf = alloca(len))) {
1109 #ifdef DEBUG
1110 bl_warn_printf(BL_DEBUG_TAG " alloca failed\n");
1111 #endif
1112 return;
1113 }
1114
1115 bl_snprintf(buf, len, "uim:%s", engine_name);
1116
1117 /*
1118 * we don't use uim_change_input_method_engine(), since it cannot
1119 * specify encoding.
1120 */
1121
1122 if (strcmp(request, "im_change_this_text_area_only") == 0) {
1123 if (focused_uim) {
1124 (*focused_uim->im.listener->im_changed)(focused_uim->im.listener->self, buf);
1125 }
1126 } else if (strcmp(request, "im_change_whole_desktop") == 0 ||
1127 strcmp(request, "im_change_this_application_only") == 0) {
1128 im_uim_t *uim;
1129
1130 uim = uim_list;
1131 while (uim) {
1132 (*uim->im.listener->im_changed)(uim->im.listener->self, buf);
1133 uim = bl_slist_next(uim);
1134 }
1135 }
1136 }
1137
helper_update_custom(char * custom,char * value)1138 static void helper_update_custom(char *custom, char *value) {
1139 im_uim_t *uim;
1140
1141 uim = uim_list;
1142 while (uim) {
1143 uim_prop_update_custom(uim->context, custom, value);
1144 uim = bl_slist_next(uim);
1145 }
1146 }
1147
helper_commit_string(u_char * str)1148 static void helper_commit_string(u_char *str /* UTF-8? */) {
1149 ef_parser_t *parser_utf8;
1150
1151 if (!focused_uim) {
1152 return;
1153 }
1154
1155 if (focused_uim->term_encoding == VT_UTF8) {
1156 (*focused_uim->im.listener->write_to_term)(focused_uim->im.listener->self, str, strlen(str),
1157 NULL);
1158 return;
1159 }
1160
1161 if (!(parser_utf8 = (*syms->vt_char_encoding_parser_new)(VT_UTF8))) {
1162 return;
1163 }
1164 (*focused_uim->im.listener->write_to_term)(focused_uim->im.listener->self, str, strlen(str),
1165 parser_utf8);
1166 (*parser_utf8->destroy)(parser_utf8);
1167 }
1168
helper_read_handler(void)1169 static void helper_read_handler(void) {
1170 char *message;
1171
1172 uim_helper_read_proc(helper_fd);
1173
1174 while ((message = uim_helper_get_message())) {
1175 char *first_line;
1176 char *second_line;
1177
1178 #ifdef IM_UIM_DEBUG
1179 bl_debug_printf("message recieved from helper: %s\n", message);
1180 #endif
1181
1182 if ((first_line = bl_str_sep(&message, "\n"))) {
1183 if (strcmp(first_line, "prop_activate") == 0) {
1184 second_line = bl_str_sep(&message, "\n");
1185 if (second_line && focused_uim) {
1186 uim_prop_activate(focused_uim->context, second_line);
1187 }
1188 } else if (strcmp(first_line, "im_list_get") == 0) {
1189 helper_send_imlist();
1190 } else if (strncmp(first_line, "im_change_", 10) == 0) {
1191 if ((second_line = bl_str_sep(&message, "\n"))) {
1192 helper_im_changed(first_line, second_line);
1193 }
1194 } else if (strcmp(first_line, "prop_update_custom") == 0) {
1195 if ((second_line = bl_str_sep(&message, "\n"))) {
1196 helper_update_custom(second_line, message);
1197 }
1198 } else if (strcmp(first_line, "focus_in") == 0) {
1199 focused_uim = NULL;
1200 } else if (strcmp(first_line, "commit_string") == 0) {
1201 if ((second_line = bl_str_sep(&message, "\n"))) {
1202 helper_commit_string(second_line);
1203 }
1204 }
1205
1206 message = first_line; /* for free() */
1207 }
1208
1209 free(message);
1210 }
1211 }
1212
1213 /* --- global functions --- */
1214
im_uim_new(u_int64_t magic,vt_char_encoding_t term_encoding,ui_im_export_syms_t * export_syms,char * engine,u_int mod_ignore_mask)1215 ui_im_t *im_uim_new(u_int64_t magic, vt_char_encoding_t term_encoding,
1216 ui_im_export_syms_t *export_syms, char *engine, u_int mod_ignore_mask) {
1217 im_uim_t *uim;
1218 char *encoding_name;
1219 vt_char_encoding_t encoding;
1220
1221 if (magic != (u_int64_t)IM_API_COMPAT_CHECK_MAGIC) {
1222 bl_error_printf("Incompatible input method API.\n");
1223
1224 return NULL;
1225 }
1226
1227 uim = NULL;
1228 encoding_name = NULL;
1229
1230 if (getenv("MOD_KEY_DEBUG")) {
1231 mod_key_debug = 1;
1232 }
1233
1234 #if 1
1235 #define RESTORE_LOCALE
1236 #endif
1237
1238 if (!uim_scm_is_initialized()) {
1239 #ifdef RESTORE_LOCALE
1240 /*
1241 * Workaround against make_locale() of m17nlib.
1242 */
1243 char *cur_locale;
1244
1245 if ((cur_locale = alloca(strlen(bl_get_locale()) + 1))) {
1246 strcpy(cur_locale, bl_get_locale());
1247 }
1248 #endif
1249
1250 if (uim_init() == -1) {
1251 #ifdef DEBUG
1252 bl_warn_printf(BL_DEBUG_TAG " failed to initialize uim.");
1253 #endif
1254
1255 return NULL;
1256 }
1257
1258 #ifdef RESTORE_LOCALE
1259 /* restoring */
1260 /*
1261 * TODO: remove valgrind warning.
1262 * The memory space pointed to by sys_locale in bl_locale.c
1263 * was freed by setlocale() in m17nlib.
1264 */
1265 bl_locale_init(cur_locale);
1266 #endif
1267
1268 syms = export_syms;
1269
1270 initialized = 1;
1271 } else if (!initialized) {
1272 initialized = -1;
1273 }
1274
1275 /*
1276 * create I/O chanel for uim_helper_server
1277 */
1278 if (helper_fd == -1 && syms && syms->ui_event_source_add_fd && syms->ui_event_source_remove_fd) {
1279 helper_fd = uim_helper_init_client_fd(helper_disconnected);
1280
1281 (*syms->ui_event_source_add_fd)(helper_fd, helper_read_handler);
1282 }
1283
1284 if ((engine == NULL) || (strlen(engine) == 0)) {
1285 /*
1286 * The returned string's storage is invalidated when we
1287 * call uim next, so we need to make a copy.
1288 */
1289 char *p = (char *)uim_get_default_im_name(bl_get_locale());
1290
1291 if ((engine = alloca(strlen(p) + 1))) {
1292 strcpy(engine, p);
1293 }
1294 }
1295
1296 if (!find_engine(engine, &encoding_name)) {
1297 bl_error_printf("%s: No such conversion engine.\n", engine);
1298 goto error;
1299 }
1300
1301 if ((encoding = (*syms->vt_get_char_encoding)(encoding_name)) == VT_UNKNOWN_ENCODING) {
1302 #ifdef DEBUG
1303 bl_warn_printf(BL_DEBUG_TAG " %s is unknown encoding.\n", encoding_name);
1304 #endif
1305 goto error;
1306 }
1307
1308 if (!(uim = calloc(1, sizeof(im_uim_t)))) {
1309 #ifdef DEBUG
1310 bl_warn_printf(BL_DEBUG_TAG " malloc failed.\n");
1311 #endif
1312
1313 goto error;
1314 }
1315
1316 uim->term_encoding = term_encoding;
1317 uim->encoding_name = encoding_name;
1318 uim->mod_ignore_mask = mod_ignore_mask;
1319
1320 if (uim->term_encoding != encoding) {
1321 if (!(uim->parser_uim = (*syms->vt_char_encoding_parser_new)(encoding))) {
1322 goto error;
1323 }
1324
1325 if (!(uim->conv = (*syms->vt_char_encoding_conv_new)(term_encoding))) {
1326 goto error;
1327 }
1328 }
1329
1330 if (!(uim->parser_term = (*syms->vt_char_encoding_parser_new)(term_encoding))) {
1331 goto error;
1332 }
1333
1334 if (!(uim->context = uim_create_context(uim, encoding_name, NULL, engine, NULL, commit))) {
1335 #ifdef DEBUG
1336 bl_warn_printf(BL_DEBUG_TAG " could not create uim context.\n");
1337 #endif
1338
1339 goto error;
1340 }
1341
1342 uim->is_mozc = (strcmp(engine, "mozc") == 0);
1343
1344 uim_set_preedit_cb(uim->context, preedit_clear, preedit_pushback, preedit_update);
1345
1346 uim_set_candidate_selector_cb(uim->context, candidate_activate, candidate_select,
1347 candidate_shift_page, candidate_deactivate);
1348
1349 uim_set_prop_list_update_cb(uim->context, prop_list_update);
1350 uim_set_prop_label_update_cb(uim->context, prop_label_update);
1351
1352 #ifdef SHOW_STATUS_SCREEN
1353 uim_set_mode_cb(uim->context, mode_update);
1354 #endif
1355
1356 focused_uim = uim;
1357 uim_prop_list_update(uim->context);
1358
1359 /*
1360 * set methods of ui_im_t
1361 */
1362 uim->im.destroy = destroy;
1363 uim->im.key_event = key_event;
1364 uim->im.switch_mode = switch_mode;
1365 uim->im.is_active = is_active;
1366 uim->im.focused = focused;
1367 uim->im.unfocused = unfocused;
1368
1369 bl_slist_insert_head(uim_list, uim);
1370
1371 ref_count++;
1372
1373 #ifdef IM_UIM_DEBUG
1374 bl_debug_printf("New object was created. ref_count is %d.\n", ref_count);
1375 #endif
1376
1377 return (ui_im_t *)uim;
1378
1379 error:
1380 if (helper_fd != -1) {
1381 (*syms->ui_event_source_remove_fd)(helper_fd);
1382
1383 uim_helper_close_client_fd(helper_fd);
1384
1385 helper_fd = -1;
1386 }
1387
1388 if (initialized > 0 && ref_count == 0) {
1389 uim_quit();
1390 initialized = 0;
1391 }
1392
1393 free(encoding_name);
1394
1395 if (uim) {
1396 if (uim->parser_uim) {
1397 (*uim->parser_uim->destroy)(uim->parser_uim);
1398 }
1399
1400 if (uim->parser_term) {
1401 (*uim->parser_term->destroy)(uim->parser_term);
1402 }
1403
1404 if (uim->conv) {
1405 (*uim->conv->destroy)(uim->conv);
1406 }
1407
1408 free(uim);
1409 }
1410
1411 return NULL;
1412 }
1413
1414 /* --- API for external tools --- */
1415
im_uim_get_info(char * locale,char * encoding)1416 im_info_t *im_uim_get_info(char *locale, char *encoding) {
1417 im_info_t *result = NULL;
1418 uim_context u;
1419 int i;
1420 int uim_init_done;
1421
1422 if (uim_scm_is_initialized()) {
1423 uim_init_done = 0;
1424 } else if (uim_init() == -1) {
1425 #ifdef DEBUG
1426 bl_warn_printf(BL_DEBUG_TAG " failed to initialize uim.");
1427 #endif
1428
1429 return NULL;
1430 } else {
1431 uim_init_done = 1;
1432 }
1433
1434 if (!(u = uim_create_context(NULL, "UTF-8", NULL, NULL, NULL, NULL))) {
1435 goto error;
1436 }
1437
1438 if (!(result = malloc(sizeof(im_info_t)))) {
1439 goto error;
1440 }
1441
1442 result->num_args = uim_get_nr_im(u) + 1;
1443 result->args = calloc(result->num_args, sizeof(char *));
1444 result->readable_args = calloc(result->num_args, sizeof(char *));
1445 if (result->args == NULL || result->readable_args == NULL) {
1446 goto error;
1447 }
1448
1449 result->args[0] = strdup("");
1450 result->readable_args[0] = strdup(uim_get_default_im_name(locale));
1451
1452 for (i = 1; i < result->num_args; i++) {
1453 const char *im_name;
1454 const char *lang_id;
1455 size_t len;
1456
1457 im_name = uim_get_im_name(u, i - 1);
1458 lang_id = uim_get_im_language(u, i - 1);
1459
1460 result->args[i] = strdup(im_name);
1461
1462 len = strlen(im_name) + strlen(lang_id) + 4;
1463
1464 if ((result->readable_args[i] = malloc(len))) {
1465 bl_snprintf(result->readable_args[i], len, "%s (%s)", im_name, lang_id);
1466 } else {
1467 result->readable_args[i] = strdup("error");
1468 }
1469 }
1470
1471 uim_release_context(u);
1472
1473 if (uim_init_done) {
1474 uim_quit();
1475 }
1476
1477 result->id = strdup("uim");
1478 result->name = strdup("uim");
1479
1480 return result;
1481
1482 error:
1483
1484 if (u) {
1485 uim_release_context(u);
1486 }
1487
1488 if (uim_init_done) {
1489 uim_quit();
1490 }
1491
1492 if (result) {
1493 if (result->args) {
1494 free(result->args);
1495 }
1496
1497 if (result->readable_args) {
1498 free(result->readable_args);
1499 }
1500
1501 free(result);
1502 }
1503
1504 return NULL;
1505 }
1506