1 /* -*- c-basic-offset:2; tab-width:2; indent-tabs-mode:nil -*- */
2 
3 #include <stdio.h>
4 
5 #include <pobl/bl_mem.h>  /* malloc/alloca/free */
6 #include <pobl/bl_str.h>  /* strdup/bl_str_sep/bl_snprintf */
7 #include <pobl/bl_util.h> /* DIGIT_STR_LEN */
8 #include <pobl/bl_conf_io.h>
9 
10 #include <ui_im.h>
11 
12 #include "../im_info.h"
13 #include "ef_str_parser.h"
14 #include "dict.h"
15 
16 #if 0
17 #define IM_SKK_DEBUG 1
18 #endif
19 
20 #define MAX_CAPTION_LEN 64
21 
22 typedef u_int16_t wchar;
23 
24 typedef enum input_mode {
25   HIRAGANA,
26   KATAKANA,
27   ALPHABET_FULL,
28   ALPHABET,
29   MAX_INPUT_MODE
30 
31 } input_mode_t;
32 
33 typedef struct im_skk {
34   /* input method common object */
35   ui_im_t im;
36 
37   int is_enabled;
38   /*
39    * 1: direct input 2: convert 3: convert with okurigana
40    * 4: convert with sokuon okurigana
41    */
42   int is_preediting;
43 
44   vt_char_encoding_t term_encoding;
45 
46   char *encoding_name; /* encoding of conversion engine */
47 
48   /* conv is NULL if term_encoding == skk encoding */
49   ef_parser_t *parser_term; /* for term encoding */
50   ef_conv_t *conv;          /* for term encoding */
51 
52   ef_char_t preedit[MAX_CAPTION_LEN];
53   u_int preedit_len;
54 
55   void *candidate;
56 
57   char *status[MAX_INPUT_MODE];
58 
59   int dan;
60   int prev_dan;
61 
62   input_mode_t mode;
63 
64   int8_t sticky_shift;
65   int8_t start_candidate;
66 
67   int8_t is_editing_new_word;
68   ef_char_t new_word[MAX_CAPTION_LEN];
69   u_int new_word_len;
70 
71   ef_char_t preedit_orig[MAX_CAPTION_LEN];
72   u_int preedit_orig_len;
73   int is_preediting_orig;
74   int prev_dan_orig;
75   input_mode_t mode_orig;
76   ef_char_t visual_chars[2];
77 
78   void *completion;
79 
80 } im_skk_t;
81 
82 /* --- static variables --- */
83 
84 ui_im_export_syms_t *syms = NULL; /* mlterm internal symbols */
85 static int ref_count = 0;
86 static KeySym sticky_shift_ksym;
87 static int sticky_shift_ch;
88 
89 /* --- static functions --- */
90 
preedit_add(im_skk_t * skk,wchar wch)91 static void preedit_add(im_skk_t *skk, wchar wch) {
92   ef_char_t ch;
93 
94   if (skk->preedit_len >= sizeof(skk->preedit) / sizeof(skk->preedit[0])) {
95     return;
96   }
97 
98   if (wch > 0xff) {
99     if (skk->mode == KATAKANA && 0xa4a1 <= wch && wch <= 0xa4f3) {
100       wch += 0x100;
101     }
102 
103     ch.ch[0] = (wch >> 8) & 0x7f;
104     ch.ch[1] = wch & 0x7f;
105     ch.size = 2;
106     ch.cs = JISX0208_1983;
107   } else {
108     ch.ch[0] = wch;
109     ch.size = 1;
110     ch.cs = US_ASCII;
111   }
112   ch.property = 0;
113 
114   skk->preedit[skk->preedit_len++] = ch;
115 }
116 
preedit_destroy(im_skk_t * skk)117 static void preedit_destroy(im_skk_t *skk) { --skk->preedit_len; }
118 
119 static void candidate_clear(im_skk_t *skk);
120 
preedit_clear(im_skk_t * skk)121 static void preedit_clear(im_skk_t *skk) {
122   if (skk->is_preediting && skk->mode == ALPHABET) {
123     skk->mode = HIRAGANA;
124   }
125 
126   skk->preedit_len = 0;
127   skk->is_preediting = 0;
128   skk->dan = 0;
129   skk->prev_dan = 0;
130 
131   candidate_clear(skk);
132 }
133 
preedit_to_visual(im_skk_t * skk)134 static void preedit_to_visual(im_skk_t *skk) {
135   if (skk->prev_dan) {
136     if (skk->is_preediting == 4) {
137       skk->preedit[skk->preedit_len] = skk->visual_chars[1];
138       skk->preedit[skk->preedit_len - 1] = skk->visual_chars[0];
139       skk->preedit_len++;
140     } else {
141       skk->preedit[skk->preedit_len - 1] = skk->visual_chars[0];
142     }
143   }
144 }
145 
preedit_backup_visual_chars(im_skk_t * skk)146 static void preedit_backup_visual_chars(im_skk_t *skk) {
147   if (skk->prev_dan) {
148     if (skk->is_preediting == 4) {
149       skk->visual_chars[1] = skk->preedit[skk->preedit_len - 1];
150       skk->visual_chars[0] = skk->preedit[skk->preedit_len - 2];
151     } else {
152       skk->visual_chars[0] = skk->preedit[skk->preedit_len - 1];
153     }
154   }
155 }
156 
preedit(im_skk_t * skk,ef_char_t * preedit,u_int preedit_len,int rev_len,char * candidateword,size_t candidateword_len,char * pos)157 static void preedit(im_skk_t *skk, ef_char_t *preedit, u_int preedit_len, int rev_len,
158                     char *candidateword /* already converted to term encoding */,
159                     /* 0 means the length of candidateword is 0 or necessity of strlen() */
160                     size_t candidateword_len,
161                     char *pos) {
162   int x;
163   int y;
164   int rev_pos = 0;
165 
166   if (skk->preedit_orig_len > 0) {
167     ef_char_t *p;
168 
169     if ((p = alloca(sizeof(*p) * (skk->preedit_orig_len + 1 + preedit_len + skk->new_word_len)))) {
170       memcpy(p, skk->preedit_orig, sizeof(*p) * skk->preedit_orig_len);
171       p[skk->preedit_orig_len].ch[0] = ':';
172       p[skk->preedit_orig_len].size = 1;
173       p[skk->preedit_orig_len].property = 0;
174       p[skk->preedit_orig_len].cs = US_ASCII;
175 
176       if (skk->new_word_len > 0) {
177         memcpy(p + skk->preedit_orig_len + 1, skk->new_word, sizeof(*p) * skk->new_word_len);
178       }
179 
180       if (preedit_len > 0) {
181         memcpy(p + skk->preedit_orig_len + 1 + skk->new_word_len, preedit,
182                preedit_len * sizeof(*p));
183       }
184 
185       preedit = p;
186       rev_pos = skk->preedit_orig_len + 1 + skk->new_word_len;
187       preedit_len += rev_pos;
188     }
189   }
190 
191   if (preedit == NULL) {
192     goto candidate;
193   } else if (preedit_len == 0) {
194     if (skk->im.preedit.filled_len > 0) {
195       /* Stop preediting. */
196       skk->im.preedit.filled_len = 0;
197     }
198   } else {
199     ef_char_t ch;
200     vt_char_t *p;
201     size_t pos_len;
202     u_int count;
203 
204     pos_len = strlen(pos);
205 
206     if ((p = realloc(skk->im.preedit.chars, sizeof(vt_char_t) * (preedit_len + pos_len))) == NULL) {
207       return;
208     }
209 
210     (*syms->vt_str_init)(skk->im.preedit.chars = p,
211                          skk->im.preedit.num_chars = (preedit_len + pos_len));
212 
213     skk->im.preedit.filled_len = 0;
214     for (count = 0; count < preedit_len; count++) {
215       int is_fullwidth;
216       int is_comb;
217 
218       ch = preedit[count];
219 
220       if ((*syms->vt_convert_to_internal_ch)(skk->im.vtparser, &ch) <= 0) {
221         continue;
222       }
223 
224       if (ch.property & EF_FULLWIDTH) {
225         is_fullwidth = 1;
226       } else if (ch.property & EF_AWIDTH) {
227         /* TODO: check col_size_of_width_a */
228         is_fullwidth = 1;
229       } else {
230         is_fullwidth = IS_FULLWIDTH_CS(ch.cs);
231       }
232 
233       if (ch.property & EF_COMBINING) {
234         is_comb = 1;
235 
236         if ((*syms->vt_char_combine)(p - 1, ef_char_to_int(&ch), ch.cs, is_fullwidth,
237                                      (ch.property & EF_AWIDTH) ? 1 : 0, is_comb,
238                                      VT_FG_COLOR, VT_BG_COLOR, 0, 0, LS_UNDERLINE_SINGLE, 0, 0)) {
239           continue;
240         }
241 
242         /*
243          * if combining failed , char is normally appended.
244          */
245       } else {
246         is_comb = 0;
247       }
248 
249       if (rev_pos <= skk->im.preedit.filled_len && skk->im.preedit.filled_len < rev_pos + rev_len) {
250         (*syms->vt_char_set)(p, ef_char_to_int(&ch), ch.cs, is_fullwidth,
251                              (ch.property & EF_AWIDTH) ? 1 : 0, is_comb, VT_BG_COLOR,
252                              VT_FG_COLOR, 0, 0, LS_UNDERLINE_SINGLE, 0, 0);
253       } else {
254         (*syms->vt_char_set)(p, ef_char_to_int(&ch), ch.cs, is_fullwidth,
255                              (ch.property & EF_AWIDTH) ? 1 : 0, is_comb, VT_FG_COLOR,
256                              VT_BG_COLOR, 0, 0, LS_UNDERLINE_SINGLE, 0, 0);
257       }
258 
259       p++;
260       skk->im.preedit.filled_len++;
261     }
262 
263     for (; pos_len > 0; pos_len--) {
264       (*syms->vt_char_set)(p++, *(pos++), US_ASCII, 0, 0, 0, VT_FG_COLOR, VT_BG_COLOR, 0, 0,
265                            LS_UNDERLINE_SINGLE, 0, 0);
266       skk->im.preedit.filled_len++;
267     }
268   }
269 
270   (*skk->im.listener->draw_preedit_str)(skk->im.listener->self, skk->im.preedit.chars,
271                                         skk->im.preedit.filled_len, skk->im.preedit.cursor_offset);
272 
273 candidate:
274   if (candidateword == NULL) {
275     return;
276   } else if (candidateword_len == 0 && strlen(candidateword) == 0) {
277     if (skk->im.stat_screen) {
278       (*skk->im.stat_screen->destroy)(skk->im.stat_screen);
279       skk->im.stat_screen = NULL;
280     }
281   } else {
282     u_char *tmp = NULL;
283 
284     (*skk->im.listener->get_spot)(skk->im.listener->self, skk->im.preedit.chars,
285                                   skk->im.preedit.segment_offset, &x, &y);
286 
287     if (skk->im.stat_screen == NULL) {
288       if (!(skk->im.stat_screen = (*syms->ui_im_status_screen_new)(
289                 skk->im.disp, skk->im.font_man, skk->im.color_man, skk->im.vtparser,
290                 (*skk->im.listener->is_vertical)(skk->im.listener->self),
291                 (*skk->im.listener->get_line_height)(skk->im.listener->self), x, y))) {
292 #ifdef DEBUG
293         bl_warn_printf(BL_DEBUG_TAG " ui_im_status_screen_new() failed.\n");
294 #endif
295 
296         return;
297       }
298     } else {
299       (*skk->im.stat_screen->show)(skk->im.stat_screen);
300       (*skk->im.stat_screen->set_spot)(skk->im.stat_screen, x, y);
301     }
302 
303     (*skk->im.stat_screen->set)(skk->im.stat_screen, skk->parser_term, candidateword);
304 
305     if (tmp) {
306       free(tmp);
307     }
308   }
309 }
310 
commit(im_skk_t * skk)311 static void commit(im_skk_t *skk) {
312   (*skk->im.listener->write_to_term)(skk->im.listener->self,
313                                      (u_char*)skk->preedit, skk->preedit_len * sizeof(ef_char_t),
314                                      ef_str_parser_get());
315 }
316 
insert_char(im_skk_t * skk,u_char key_char)317 static int insert_char(im_skk_t *skk, u_char key_char) {
318   static struct {
319     wchar a;
320     wchar i;
321     wchar u;
322     wchar e;
323     wchar o;
324 
325   } kana_table[] = {
326       /* a */ /* i */ /* u */ /* e */ /* o */
327       {0xa4a2, 0xa4a4, 0xa4a6, 0xa4a8, 0xa4aa},
328       {0xa4d0, 0xa4d3, 0xa4d6, 0xa4d9, 0xa4dc}, /* b */
329       {0xa4ab, 0xa4ad, 0xa4af, 0xa4b1, 0xa4b3}, /* c */
330       {0xa4c0, 0xa4c2, 0xa4c5, 0xa4c7, 0xa4c9}, /* d */
331       {0xa4e3, 0xa4a3, 0xa4e5, 0xa4a7, 0xa4e7}, /* xy/dh */
332       {0, 0, 0xa4d5, 0, 0,},                    /* f */
333       {0xa4ac, 0xa4ae, 0xa4b0, 0xa4b2, 0xa4b4}, /* g */
334       {0xa4cf, 0xa4d2, 0xa4d5, 0xa4d8, 0xa4db}, /* h */
335       {0xa4e3, 0, 0xa4e5, 0xa4a7, 0xa4e7},      /* ch/sh */
336       {0, 0xa4b8, 0, 0, 0,},                    /* j */
337       {0xa4ab, 0xa4ad, 0xa4af, 0xa4b1, 0xa4b3}, /* k */
338       {0xa4a1, 0xa4a3, 0xa4a5, 0xa4a7, 0xa4a9}, /* l */
339       {0xa4de, 0xa4df, 0xa4e0, 0xa4e1, 0xa4e2}, /* m */
340       {0xa4ca, 0xa4cb, 0xa4cc, 0xa4cd, 0xa4ce}, /* n */
341       {0, 0, 0, 0, 0,},
342       {0xa4d1, 0xa4d4, 0xa4d7, 0xa4da, 0xa4dd}, /* p */
343       {0, 0, 0, 0, 0,},
344       {0xa4e9, 0xa4ea, 0xa4eb, 0xa4ec, 0xa4ed}, /* r */
345       {0xa4b5, 0xa4b7, 0xa4b9, 0xa4bb, 0xa4bd}, /* s */
346       {0xa4bf, 0xa4c1, 0xa4c4, 0xa4c6, 0xa4c8}, /* t */
347       {0, 0, 0, 0, 0,},
348       {0, 0, 0, 0, 0,},
349       {0xa4ef, 0xa4f0, 0, 0xa4f1, 0xa4f2},      /* w */
350       {0xa4a1, 0xa4a3, 0xa4a5, 0xa4a7, 0xa4a9}, /* x */
351       {0xa4e4, 0, 0xa4e6, 0, 0xa4e8},           /* y */
352       {0xa4b6, 0xa4b8, 0xa4ba, 0xa4bc, 0xa4be}, /* z */
353   };
354   static wchar sign_table1[] = {
355       0xa1aa, 0xa1c9, 0xa1f4, 0xa1f0, 0xa1f3, 0xa1f5, 0xa1c7, 0xa1ca, 0xa1cb, 0xa1f6, 0xa1dc,
356       0xa1a4, 0xa1bc, 0xa1a3, 0xa1bf, 0xa3b0, 0xa3b1, 0xa3b2, 0xa3b3, 0xa3b4, 0xa3b5, 0xa3b6,
357       0xa3b7, 0xa3b8, 0xa3b9, 0xa1a7, 0xa1a8, 0xa1e3, 0xa1e1, 0xa1e4, 0xa1a9, 0xa1f7,
358   };
359   static wchar sign_table2[] = {
360       0xa1ce, 0xa1ef, 0xa1cf, 0xa1b0, 0xa1b2,
361   };
362   static wchar sign_table3[] = {
363       0xa1d0, 0xa1c3, 0xa1d1, 0xa1c1,
364   };
365   wchar wch;
366 
367   if (skk->dan) {
368     preedit_destroy(skk);
369   }
370 
371   if (key_char == 'a' || key_char == 'i' || key_char == 'u' || key_char == 'e' || key_char == 'o') {
372     if (skk->dan == 'f' - 'a') {
373       if (key_char != 'u') {
374         preedit_add(skk, 0xa4d5); /* hu */
375         skk->dan = 'x' - 'a';
376       }
377     } else if (skk->dan == 'j' - 'a') {
378       if (key_char != 'i') {
379         preedit_add(skk, 0xa4b8); /* zi */
380         skk->dan = 'e' - 'a';
381       }
382     }
383 
384     if (key_char == 'a') {
385       wch = kana_table[skk->dan].a;
386       skk->dan = 0;
387     } else if (key_char == 'i') {
388       if (skk->dan == 'i' - 'a') {
389         skk->dan = 0;
390 
391         return 0; /* shi/chi */
392       }
393 
394       wch = kana_table[skk->dan].i;
395       skk->dan = 0;
396     } else if (key_char == 'u') {
397       if (skk->dan == 'j' - 'a') {
398         preedit_add(skk, 0xa4b8); /* zi */
399         skk->dan = 'e' - 'a';
400       }
401       wch = kana_table[skk->dan].u;
402       skk->dan = 0;
403     } else if (key_char == 'e') {
404       if (skk->dan == 'f' - 'a') {
405         preedit_add(skk, 0xa4d5); /* hu */
406         skk->dan = 'x' - 'a';
407       } else if (skk->dan == 'j' - 'a') {
408         preedit_add(skk, 0xa4b8); /* zi */
409         skk->dan = 'x' - 'a';
410       }
411       wch = kana_table[skk->dan].e;
412       skk->dan = 0;
413     } else /* if( key_char == 'o') */
414     {
415       if (skk->dan == 'f' - 'a') {
416         preedit_add(skk, 0xa4d5); /* hu */
417         skk->dan = 'x' - 'a';
418       } else if (skk->dan == 'j' - 'a') {
419         preedit_add(skk, 0xa4b8); /* zi */
420         skk->dan = 'e' - 'a';
421       }
422       wch = kana_table[skk->dan].o;
423       skk->dan = 0;
424     }
425   } else if (('!' <= key_char && key_char <= '@') || ('[' <= key_char && key_char <= '_') ||
426              ('{' <= key_char && key_char <= '~')) {
427     if (skk->dan) {
428       preedit_add(skk, skk->dan + 'a');
429       skk->dan = 0;
430     }
431 
432     if (key_char <= '@') {
433       wch = sign_table1[key_char - '!'];
434     } else if (key_char <= '_') {
435       wch = sign_table2[key_char - '['];
436     } else {
437       wch = sign_table3[key_char - '{'];
438     }
439   } else {
440     if (skk->dan == 'n' - 'a' && key_char != 'a' && key_char != 'i' && key_char != 'u' &&
441         key_char != 'e' && key_char != 'o' && key_char != 'y') {
442       if (key_char == 'n') {
443         wch = 0xa4f3; /* n */
444         skk->dan = 0;
445       } else {
446         preedit_add(skk, 0xa4f3);
447         wch = key_char;
448         skk->dan = key_char - 'a';
449       }
450     } else if (key_char == skk->dan + 'a') {
451       preedit_add(skk, 0xa4c3);
452       wch = key_char;
453     } else if (key_char == 'y') {
454       if (skk->dan == 'k' - 'a') {
455         preedit_add(skk, 0xa4ad); /* ki */
456         skk->dan = 'x' - 'a';
457       } else if (skk->dan == 'g' - 'a') {
458         preedit_add(skk, 0xa4ae); /* gi */
459         skk->dan = 'x' - 'a';
460       } else if (skk->dan == 's' - 'a') {
461         preedit_add(skk, 0xa4b7); /* si */
462         skk->dan = 'x' - 'a';
463       } else if (skk->dan == 'z' - 'a') {
464         preedit_add(skk, 0xa4b8); /* zi */
465         skk->dan = 'x' - 'a';
466       } else if (skk->dan == 't' - 'a' || skk->dan == 'c' - 'a') {
467         preedit_add(skk, 0xa4c1); /* ti */
468         skk->dan = 'x' - 'a';
469       } else if (skk->dan == 'd' - 'a') {
470         preedit_add(skk, 0xa4c2); /* di */
471         skk->dan = 'x' - 'a';
472       } else if (skk->dan == 'n' - 'a') {
473         preedit_add(skk, 0xa4cb); /* ni */
474         skk->dan = 'x' - 'a';
475       } else if (skk->dan == 'h' - 'a') {
476         preedit_add(skk, 0xa4d2); /* hi */
477         skk->dan = 'x' - 'a';
478       } else if (skk->dan == 'b' - 'a') {
479         preedit_add(skk, 0xa4d3); /* bi */
480         skk->dan = 'x' - 'a';
481       } else if (skk->dan == 'p' - 'a') {
482         preedit_add(skk, 0xa4d4); /* pi */
483         skk->dan = 'x' - 'a';
484       } else if (skk->dan == 'm' - 'a') {
485         preedit_add(skk, 0xa4df); /* mi */
486         skk->dan = 'x' - 'a';
487       } else if (skk->dan == 'r' - 'a') {
488         preedit_add(skk, 0xa4ea); /* ri */
489         skk->dan = 'x' - 'a';
490       }
491 
492       if (skk->dan == 'x' - 'a') {
493         skk->dan = 'e' - 'a';
494         wch = 'y';
495       } else if (skk->dan == 'v' - 'a') {
496         if (key_char == 'u') {
497           wch = 0xa5f4;
498           skk->dan = 0;
499         } else {
500           preedit_add(skk, 0xa5f4); /* v */
501           skk->dan = 'x' - 'a';
502 
503           return insert_char(skk, key_char);
504         }
505       } else {
506         goto normal;
507       }
508     } else if (key_char == 'h') {
509       if (skk->dan == 'c' - 'a') {
510         preedit_add(skk, 0xa4c1); /* ti */
511         skk->dan = 'i' - 'a';
512       } else if (skk->dan == 's' - 'a') {
513         preedit_add(skk, 0xa4b7); /* si */
514         skk->dan = 'i' - 'a';
515       } else if (skk->dan == 'd' - 'a') {
516         preedit_add(skk, 0xa4c7); /* di */
517         skk->dan = 'e' - 'a';
518       } else {
519         goto normal;
520       }
521 
522       wch = 'h';
523     } else {
524     normal:
525       if (skk->dan) {
526         preedit_add(skk, skk->dan + 'a');
527       }
528 
529       wch = key_char;
530 
531       if ('a' <= key_char && key_char <= 'z') {
532         skk->dan = key_char - 'a';
533       } else {
534         skk->dan = 0;
535       }
536     }
537   }
538 
539   if (wch == 0) {
540     return 1;
541   }
542 
543   preedit_add(skk, wch);
544 
545   return 0;
546 }
547 
insert_alphabet_full(im_skk_t * skk,u_char key_char)548 static void insert_alphabet_full(im_skk_t *skk, u_char key_char) {
549   if (('a' <= key_char && key_char <= 'z') || ('A' <= key_char && key_char <= 'Z') ||
550       ('0' <= key_char && key_char <= '9')) {
551     preedit_add(skk, 0x2300 + key_char + 0x8080);
552   } else if (0x20 <= key_char && key_char <= 0x7e) {
553     insert_char(skk, key_char);
554   }
555 }
556 
557 /*
558  * methods of ui_im_t
559  */
560 
destroy(ui_im_t * im)561 static void destroy(ui_im_t *im) {
562   im_skk_t *skk;
563 
564   skk = (im_skk_t*)im;
565 
566   (*skk->parser_term->destroy)(skk->parser_term);
567 
568   if (skk->conv) {
569     (*skk->conv->destroy)(skk->conv);
570   }
571 
572   free(skk->status[HIRAGANA]);
573   free(skk->status[KATAKANA]);
574   free(skk->status[ALPHABET_FULL]);
575 
576   if (skk->completion) {
577     dict_completion_finish(&skk->completion);
578   }
579 
580   if (skk->candidate) {
581     dict_candidate_finish(&skk->candidate);
582   }
583 
584   free(skk);
585 
586   ref_count--;
587 
588   if (ref_count == 0) {
589     dict_final();
590   }
591 
592 #ifdef IM_SKK_DEBUG
593   bl_debug_printf(BL_DEBUG_TAG " An object was destroyed. ref_count: %d\n", ref_count);
594 #endif
595 }
596 
switch_mode(ui_im_t * im)597 static int switch_mode(ui_im_t *im) {
598   im_skk_t *skk;
599 
600   skk = (im_skk_t*)im;
601 
602   if ((skk->is_enabled = (!skk->is_enabled))) {
603     skk->mode = HIRAGANA;
604     preedit(skk, "", 0, 0, skk->status[skk->mode], 0, "");
605   } else {
606     preedit_clear(skk);
607     preedit(skk, "", 0, 0, "", 0, "");
608   }
609 
610   return 1;
611 }
612 
start_to_register_new_word(im_skk_t * skk)613 static void start_to_register_new_word(im_skk_t *skk) {
614   memcpy(skk->preedit_orig, skk->preedit, sizeof(skk->preedit[0]) * skk->preedit_len);
615   if (skk->prev_dan) {
616     if (skk->is_preediting == 4) {
617       skk->preedit_len--;
618     }
619 
620     skk->preedit_orig[skk->preedit_len - 1].ch[0] = skk->prev_dan + 'a';
621     skk->preedit_orig[skk->preedit_len - 1].size = 1;
622     skk->preedit_orig[skk->preedit_len - 1].cs = US_ASCII;
623     skk->preedit_orig[skk->preedit_len - 1].property = 0;
624   }
625 
626   skk->preedit_orig_len = skk->preedit_len;
627   skk->is_preediting_orig = skk->is_preediting;
628   skk->dan = 0;
629   skk->prev_dan_orig = skk->prev_dan;
630   skk->mode_orig = skk->mode;
631   candidate_clear(skk);
632 
633   skk->new_word_len = 0;
634   skk->is_editing_new_word = 1;
635   preedit_clear(skk);
636   skk->is_preediting = 0;
637 }
638 
stop_to_register_new_word(im_skk_t * skk)639 static void stop_to_register_new_word(im_skk_t *skk) {
640   memcpy(skk->preedit, skk->preedit_orig, sizeof(skk->preedit_orig[0]) * skk->preedit_orig_len);
641   skk->preedit_len = skk->preedit_orig_len;
642   skk->preedit_orig_len = 0;
643   skk->dan = 0;
644   skk->prev_dan = skk->prev_dan_orig;
645   skk->mode = skk->mode_orig;
646 
647   skk->new_word_len = 0;
648   skk->is_editing_new_word = 0;
649   skk->is_preediting = skk->is_preediting_orig;
650 
651   preedit_to_visual(skk);
652 }
653 
candidate_set(im_skk_t * skk,int step)654 static void candidate_set(im_skk_t *skk, int step) {
655   if (skk->preedit_len == 0) {
656     return;
657   }
658 
659   if (skk->prev_dan) {
660     if (skk->is_preediting == 4) {
661       skk->visual_chars[1] = skk->preedit[--skk->preedit_len];
662     }
663 
664     skk->visual_chars[0] = skk->preedit[skk->preedit_len - 1];
665     skk->preedit[skk->preedit_len - 1].ch[0] = skk->prev_dan + 'a';
666     skk->preedit[skk->preedit_len - 1].size = 1;
667     skk->preedit[skk->preedit_len - 1].cs = US_ASCII;
668     skk->preedit[skk->preedit_len - 1].property = 0;
669   }
670 
671   skk->preedit_len = dict_candidate(skk->preedit, skk->preedit_len, &skk->candidate, step);
672 
673   if (!skk->candidate) {
674     if (skk->is_editing_new_word) {
675       return;
676     }
677 
678 #if 0
679     if (skk->prev_dan) {
680       skk->preedit[skk->preedit_len - 1] = skk->visual_chars[0];
681 
682       if (skk->is_preediting == 4) {
683         skk->preedit_len++;
684       }
685     }
686 #endif
687 
688     start_to_register_new_word(skk);
689 
690     return;
691   }
692 
693   if (skk->prev_dan) {
694     skk->preedit[skk->preedit_len++] = skk->visual_chars[0];
695 
696     if (skk->is_preediting == 4) {
697       skk->preedit[skk->preedit_len++] = skk->visual_chars[1];
698     }
699   }
700 
701   if (skk->dan) {
702     ef_char_t *ch;
703 
704     ch = skk->preedit + (skk->preedit_len++);
705     ch->ch[0] = skk->dan + 'a';
706     ch->cs = US_ASCII;
707     ch->size = 0;
708     ch->property = 0;
709   }
710 }
711 
candidate_unset(im_skk_t * skk)712 static void candidate_unset(im_skk_t *skk) {
713   if (skk->candidate) {
714     skk->preedit_len = dict_candidate_reset_and_finish(skk->preedit, &skk->candidate);
715   }
716 
717   preedit_to_visual(skk);
718 }
719 
candidate_clear(im_skk_t * skk)720 static void candidate_clear(im_skk_t *skk) {
721   if (skk->candidate) {
722     dict_candidate_finish(&skk->candidate);
723   }
724 }
725 
fix(im_skk_t * skk)726 static int fix(im_skk_t *skk) {
727   if (skk->preedit_len > 0) {
728     if (skk->candidate) {
729       dict_candidate_add_to_local(skk->candidate);
730     }
731 
732     if (skk->is_editing_new_word) {
733       memcpy(skk->new_word + skk->new_word_len, skk->preedit,
734              skk->preedit_len * sizeof(skk->preedit[0]));
735       skk->new_word_len += skk->preedit_len;
736       preedit(skk, "", 0, 0, skk->status[skk->mode], 0, "");
737     } else {
738       preedit(skk, "", 0, 0, skk->status[skk->mode], 0, "");
739       commit(skk);
740     }
741     preedit_clear(skk);
742     candidate_clear(skk);
743   } else if (skk->is_editing_new_word) {
744     if (skk->new_word_len > 0) {
745       dict_add_new_word_to_local(skk->preedit_orig, skk->preedit_orig_len, skk->new_word,
746                                  skk->new_word_len);
747 
748       candidate_clear(skk);
749       stop_to_register_new_word(skk);
750       candidate_set(skk, 0);
751       commit(skk);
752       preedit_clear(skk);
753       candidate_clear(skk);
754     } else {
755       stop_to_register_new_word(skk);
756       candidate_clear(skk);
757     }
758   } else {
759     return 1;
760   }
761 
762   return 0;
763 }
764 
key_event(ui_im_t * im,u_char key_char,KeySym ksym,XKeyEvent * event)765 static int key_event(ui_im_t *im, u_char key_char, KeySym ksym, XKeyEvent *event) {
766   im_skk_t *skk;
767   int ret = 0;
768   char *pos = "";
769   char *cand = NULL;
770   u_int cand_len = 0;
771   int preedited_alphabet;
772 
773   skk = (im_skk_t*)im;
774 
775   if (skk->preedit_len > 0 && !skk->candidate && (ksym == XK_Tab || ksym == XK_ISO_Left_Tab)) {
776     skk->preedit_len =
777         dict_completion(skk->preedit, skk->preedit_len, &skk->completion,
778                         ksym == XK_ISO_Left_Tab || (event->state & ShiftMask) ? -1 : 1);
779     goto end;
780   } else if (skk->completion) {
781     if ((event->state & ControlMask) && key_char == '\x07') {
782       skk->preedit_len = dict_completion_reset_and_finish(skk->preedit, &skk->completion);
783 
784       goto end;
785     } else if (ksym == XK_Shift_L || ksym == XK_Shift_R || ksym == XK_Control_L ||
786                ksym == XK_Control_R) {
787       /* Starting Shift+Tab or Control+g */
788       return 0;
789     }
790 
791     dict_completion_finish(&skk->completion);
792   }
793 
794   if (key_char == ' ' && (event->state & ShiftMask)) {
795     fix(skk);
796     switch_mode(im);
797 
798     if (!skk->is_enabled) {
799       return 0;
800     }
801 
802     goto end;
803   } else if (!skk->is_enabled) {
804     return 1;
805   }
806 
807   /* skk is enabled */
808 
809   if (skk->mode != ALPHABET && skk->mode != ALPHABET_FULL) {
810     if (sticky_shift_ch ? (key_char == sticky_shift_ch) :
811                           (sticky_shift_ksym ? (ksym == sticky_shift_ksym) : 0)) {
812       if (skk->sticky_shift) {
813         skk->sticky_shift = 0;
814       } else {
815         skk->sticky_shift = 1;
816 
817         return 0;
818       }
819     } else if (skk->sticky_shift) {
820       if ('a' <= key_char && key_char <= 'z') {
821         key_char -= 0x20;
822       }
823       event->state |= ShiftMask;
824       skk->sticky_shift = 0;
825     }
826   }
827 
828   if ((skk->mode != ALPHABET && key_char == 'l') ||
829       (skk->mode != ALPHABET_FULL && key_char == 'L')) {
830     if (!skk->is_editing_new_word) {
831       fix(skk);
832     }
833     skk->mode = (key_char == 'l') ? ALPHABET : ALPHABET_FULL;
834     cand = skk->status[skk->mode];
835   } else if (key_char == '\r' || key_char == '\n') {
836     if ((event->state & ControlMask) && key_char == '\n') {
837       if (!skk->is_editing_new_word || skk->preedit_len > 0) {
838         fix(skk);
839       }
840 
841       if (skk->mode == ALPHABET || skk->mode == ALPHABET_FULL) {
842         /* Ctrl+j */
843         skk->mode = HIRAGANA;
844         cand = skk->status[skk->mode];
845       }
846     } else {
847       preedited_alphabet = (skk->mode == ALPHABET && skk->is_preediting);
848 
849       ret = fix(skk);
850 
851       if (preedited_alphabet) {
852         cand = skk->status[skk->mode];
853       }
854     }
855   } else if (ksym == XK_BackSpace || ksym == XK_Delete || key_char == 0x08 /* Ctrl+h */) {
856     if (skk->preedit_len > 0) {
857       if (skk->candidate) {
858         candidate_unset(skk);
859         cand = skk->status[skk->mode];
860       } else {
861         skk->dan = skk->prev_dan = 0;
862         skk->is_preediting = 1;
863 
864         if (skk->preedit_len == 1) {
865           preedit_clear(skk);
866         } else {
867           preedit_destroy(skk);
868         }
869       }
870     } else if (skk->is_editing_new_word) {
871       if (skk->new_word_len > 0) {
872         skk->new_word_len--;
873       }
874     } else {
875       ret = 1;
876     }
877   } else if ((event->state & ControlMask) && key_char == '\x07') {
878     /* Ctrl+g */
879     if (skk->candidate) {
880       candidate_unset(skk);
881       candidate_clear(skk);
882       skk->dan = skk->prev_dan = 0;
883       skk->is_preediting = 1;
884     } else if (skk->is_editing_new_word && skk->preedit_len == 0) {
885       stop_to_register_new_word(skk);
886     } else {
887       preedit_clear(skk);
888     }
889     cand = skk->status[skk->mode];
890   } else if (key_char != ' ' && key_char != '\0') {
891     if (key_char < ' ') {
892       ret = 1;
893     } else if (skk->mode != ALPHABET && key_char == 'q') {
894       if (skk->mode == HIRAGANA) {
895         skk->mode = KATAKANA;
896       } else {
897         skk->mode = HIRAGANA;
898       }
899       cand = skk->status[skk->mode];
900     } else if (skk->mode != ALPHABET && !skk->is_preediting && key_char == '/') {
901       skk->is_preediting = 1;
902       skk->mode = ALPHABET;
903       cand = skk->status[skk->mode];
904     } else {
905       if (skk->candidate && !skk->dan) {
906         preedited_alphabet = (skk->mode == ALPHABET && skk->is_preediting);
907 
908         fix(skk);
909 
910         if (preedited_alphabet) {
911           cand = skk->status[skk->mode];
912         }
913       }
914 
915       if (skk->mode != ALPHABET && skk->mode != ALPHABET_FULL) {
916         while (event->state & ShiftMask) {
917           if ('A' <= key_char && key_char <= 'Z') {
918             key_char += 0x20;
919           } else if (key_char < 'a' || 'z' < key_char) {
920             break;
921           }
922 
923           if (skk->preedit_len == 0) {
924             skk->is_preediting = 1;
925           } else if (skk->is_preediting && !skk->dan) {
926             skk->is_preediting = 2;
927           }
928 
929           break;
930         }
931       }
932 
933       if (skk->mode == ALPHABET) {
934         preedit_add(skk, key_char);
935 
936         if (!skk->is_preediting) {
937           fix(skk);
938         }
939       } else if (skk->mode == ALPHABET_FULL) {
940         insert_alphabet_full(skk, key_char);
941 
942         fix(skk);
943       } else if (insert_char(skk, key_char) != 0) {
944         ret = 1;
945       } else if (skk->is_preediting >= 2) {
946         if (skk->dan) {
947           if (skk->dan == skk->prev_dan) {
948             /* sokuon */
949             skk->is_preediting++;
950           } else {
951             skk->prev_dan = skk->dan;
952           }
953         } else {
954           if (!skk->prev_dan &&
955               (key_char == 'i' || key_char == 'u' || key_char == 'e' || key_char == 'o')) {
956             skk->prev_dan = key_char - 'a';
957           }
958 
959           skk->is_preediting++;
960           candidate_set(skk, 0);
961         }
962       } else if (!skk->dan && !skk->is_preediting) {
963         fix(skk);
964       }
965     }
966   } else if (skk->is_preediting && (key_char == ' ' || ksym == XK_Up || ksym == XK_Down ||
967                                     ksym == XK_Right || ksym == XK_Left)) {
968     int update = 0;
969     int step;
970     int cur_index;
971     u_int num;
972 
973     if (!skk->candidate) {
974       if (key_char != ' ') {
975         return 1;
976       }
977 
978       step = 0;
979       skk->start_candidate = 1;
980     } else {
981       dict_candidate_get_state(skk->candidate, &cur_index, &num);
982 
983       if (ksym == XK_Left) {
984         step = -1;
985 
986         if (cur_index % CAND_UNIT == 0) {
987           update = 1;
988         }
989       } else if (ksym == XK_Up) {
990         step = -CAND_UNIT;
991         update = 1;
992       } else if (ksym == XK_Down) {
993         step = CAND_UNIT;
994         update = 1;
995       } else {
996         step = 1;
997 
998         if (cur_index == num - 1) {
999           if (!skk->is_editing_new_word) {
1000             preedit_backup_visual_chars(skk);
1001             candidate_unset(skk);
1002             candidate_clear(skk);
1003             start_to_register_new_word(skk);
1004             cand = skk->status[skk->mode];
1005 
1006             goto end;
1007           } else {
1008             update = 1;
1009           }
1010         } else if ((cur_index % CAND_UNIT == CAND_UNIT - 1) || skk->start_candidate) {
1011           update = 1;
1012         }
1013       }
1014 
1015       skk->start_candidate = 0;
1016     }
1017 
1018     preedited_alphabet = (skk->mode == ALPHABET && skk->is_preediting);
1019 
1020     candidate_set(skk, step);
1021 
1022     if (preedited_alphabet && !skk->is_preediting) {
1023       cand = skk->status[skk->mode];
1024     }
1025 
1026     if (skk->candidate) {
1027       dict_candidate_get_state(skk->candidate, &cur_index, &num);
1028 
1029       if (update) {
1030         if ((cand = alloca(1024))) {
1031           dict_candidate_get_list(skk->candidate, cand, 1024, skk->conv);
1032         }
1033       }
1034 
1035       if ((pos = alloca(4 + DIGIT_STR_LEN(int)*2 + 1))) {
1036         sprintf(pos, " [%d/%d]", cur_index + 1, num);
1037       }
1038     }
1039   } else if (skk->is_editing_new_word) {
1040     if (key_char == ' ') {
1041       preedit_add(skk, key_char);
1042       fix(skk);
1043     }
1044 
1045     ret = 0;
1046   } else {
1047     ret = 1;
1048   }
1049 
1050 end:
1051   preedit(skk, skk->preedit, skk->preedit_len, skk->is_preediting ? skk->preedit_len : 0, cand,
1052           cand_len, pos);
1053 
1054   return ret;
1055 }
1056 
is_active(ui_im_t * im)1057 static int is_active(ui_im_t *im) { return ((im_skk_t*)im)->is_enabled; }
1058 
focused(ui_im_t * im)1059 static void focused(ui_im_t *im) {
1060   im_skk_t *skk;
1061 
1062   skk = (im_skk_t*)im;
1063 
1064   if (skk->im.stat_screen) {
1065     (*skk->im.stat_screen->show)(skk->im.stat_screen);
1066   }
1067 }
1068 
unfocused(ui_im_t * im)1069 static void unfocused(ui_im_t *im) {
1070   im_skk_t *skk;
1071 
1072   skk = (im_skk_t*)im;
1073 
1074   if (skk->im.stat_screen) {
1075     (*skk->im.stat_screen->hide)(skk->im.stat_screen);
1076   }
1077 }
1078 
set_sticky_shift_key(char * key)1079 static void set_sticky_shift_key(char *key) {
1080   int digit;
1081   if (strlen(key) == 1) {
1082     sticky_shift_ch = *key;
1083   } else if (sscanf(key, "\\x%x", &digit) == 1) {
1084     sticky_shift_ch = digit;
1085   } else {
1086     sticky_shift_ksym = (*syms->XStringToKeysym)(key);
1087     sticky_shift_ch = 0;
1088 
1089     return;
1090   }
1091 
1092   sticky_shift_ksym = 0;
1093 }
1094 
1095 /* --- global functions --- */
1096 
im_skk_new(u_int64_t magic,vt_char_encoding_t term_encoding,ui_im_export_syms_t * export_syms,char * options,u_int mod_ignore_mask)1097 ui_im_t *im_skk_new(u_int64_t magic, vt_char_encoding_t term_encoding,
1098                     ui_im_export_syms_t *export_syms, char *options, u_int mod_ignore_mask) {
1099   im_skk_t *skk;
1100   ef_parser_t *parser;
1101   char *env;
1102 
1103   if (magic != (u_int64_t)IM_API_COMPAT_CHECK_MAGIC) {
1104     bl_error_printf("Incompatible input method API.\n");
1105 
1106     return NULL;
1107   }
1108 
1109   if (ref_count == 0) {
1110     syms = export_syms;
1111   }
1112 
1113   if (!(skk = calloc(1, sizeof(im_skk_t)))) {
1114 #ifdef DEBUG
1115     bl_warn_printf(BL_DEBUG_TAG " malloc failed.\n");
1116 #endif
1117 
1118     goto error;
1119   }
1120 
1121   if ((env = getenv("SKK_DICTIONARY"))) {
1122     dict_set_global(env);
1123   }
1124 
1125   if ((env = getenv("SKK_STICKY_SHIFT_KEY"))) {
1126     set_sticky_shift_key(env);
1127   }
1128 
1129   /* Same processing as skk_widget_new() in mc_im.c */
1130   if (options) {
1131 #if 1
1132     /* XXX Compat with 3.8.3 or before. */
1133     if (!strchr(options, '=')) {
1134       dict_set_global(options);
1135     } else
1136 #endif
1137     {
1138       char *next;
1139 
1140       do {
1141         if ((next = strchr(options, ','))) {
1142           *(next++) = '\0';
1143         }
1144 
1145         if (strncmp(options, "sskey=", 6) == 0) {
1146           set_sticky_shift_key(options + 6);
1147         } else if (strncmp(options, "dict=", 5) == 0) {
1148           dict_set_global(options + 5);
1149         }
1150       } while ((options = next));
1151     }
1152   }
1153 
1154   skk->term_encoding = term_encoding;
1155   skk->encoding_name = (*syms->vt_get_char_encoding_name)(term_encoding);
1156 
1157   if (!(skk->conv = (*syms->vt_char_encoding_conv_new)(term_encoding))) {
1158     goto error;
1159   }
1160 
1161   if (!(skk->parser_term = (*syms->vt_char_encoding_parser_new)(term_encoding))) {
1162     goto error;
1163   }
1164 
1165   skk->status[HIRAGANA] = "\xa4\xab\xa4\xca";
1166   skk->status[KATAKANA] = "\xa5\xab\xa5\xca";
1167   skk->status[ALPHABET_FULL] = "\xc1\xb4\xb1\xd1";
1168   skk->status[ALPHABET] = "SKK";
1169 
1170   if (term_encoding == VT_EUCJP || !(parser = (*syms->vt_char_encoding_parser_new)(VT_EUCJP))) {
1171     input_mode_t mode;
1172 
1173     for (mode = 0; mode <= ALPHABET_FULL; mode++) {
1174       skk->status[mode] = strdup(skk->status[mode]);
1175     }
1176   } else {
1177     input_mode_t mode;
1178     u_char buf[64]; /* enough for EUCJP(5bytes) -> Other CS */
1179 
1180     for (mode = 0; mode <= ALPHABET_FULL; mode++) {
1181       (*parser->init)(parser);
1182       (*parser->set_str)(parser, skk->status[mode], 5);
1183       (*skk->conv->init)(skk->conv);
1184       (*skk->conv->convert)(skk->conv, buf, sizeof(buf) - 1, parser);
1185       skk->status[mode] = strdup(buf);
1186     }
1187 
1188     (*parser->destroy)(parser);
1189   }
1190 
1191   /*
1192    * set methods of ui_im_t
1193    */
1194   skk->im.destroy = destroy;
1195   skk->im.key_event = key_event;
1196   skk->im.switch_mode = switch_mode;
1197   skk->im.is_active = is_active;
1198   skk->im.focused = focused;
1199   skk->im.unfocused = unfocused;
1200 
1201   ref_count++;
1202 
1203 #ifdef IM_SKK_DEBUG
1204   bl_debug_printf("New object was created. ref_count is %d.\n", ref_count);
1205 #endif
1206 
1207   return (ui_im_t*)skk;
1208 
1209 error:
1210   if (skk) {
1211     if (skk->parser_term) {
1212       (*skk->parser_term->destroy)(skk->parser_term);
1213     }
1214 
1215     if (skk->conv) {
1216       (*skk->conv->destroy)(skk->conv);
1217     }
1218 
1219     free(skk);
1220   }
1221 
1222   return NULL;
1223 }
1224 
1225 /* --- API for external tools --- */
1226 
im_skk_get_info(char * locale,char * encoding)1227 im_info_t *im_skk_get_info(char *locale, char *encoding) {
1228   im_info_t *result;
1229 
1230   if ((result = malloc(sizeof(im_info_t)))) {
1231     result->id = strdup("skk");
1232     result->name = strdup("Skk");
1233     result->num_args = 0;
1234     result->args = NULL;
1235     result->readable_args = NULL;
1236   }
1237 
1238   return result;
1239 }
1240