1 /*
2 
3   Copyright (c) 2010-2013 uim Project http://code.google.com/p/uim/
4 
5   All rights reserved.
6 
7   Redistribution and use in source and binary forms, with or without
8   modification, are permitted provided that the following conditions
9   are met:
10 
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. Neither the name of authors nor the names of its contributors
17      may be used to endorse or promote products derived from this software
18      without specific prior written permission.
19 
20   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 COPYRIGHT HOLDERS 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 
34 //#include <config.h>
35 
36 #include <stdio.h>
37 #include <stdlib.h>
38 #include <string.h>
39 #include <ctype.h>
40 
41 #include "uim.h"
42 #include "uim-scm.h"
43 #include "uim-scm-abbrev.h"
44 #include "uim-util.h"
45 #if UIM_VERSION_REQUIRE(1, 6, 0)
46 # include "dynlib.h"
47 #else
48 # include "plugin.h"
49 #endif
50 
51 #include "base/init_mozc.h"
52 #include "base/port.h"
53 #include "base/util.h"
54 #include "protocol/config.pb.h"
55 #include "protocol/commands.pb.h"
56 #include "client/client.h"
57 #include "unix/uim/key_translator.h"
58 
59 // use server/client session
60 #include "base/util.h"
61 
62 #define USE_CASCADING_CANDIDATES	0
63 
64 #include <map>
65 #include <ext/hash_map>
66 using __gnu_cxx::hash_map;
67 static char **argv;
68 
69 // for every 5 minutes, call SyncData
70 const uint64 kSyncDataInterval = 5 * 60;
71 // An ID for a candidate which is not associated with a text.
72 const int32 kBadCandidateId = -1;
73 
GetTime()74 uint64 GetTime() {
75   return static_cast<uint64>(time(NULL));
76 }
77 
78 namespace mozc {
79 
80 namespace client {
81 class ClientInterface;
82 }
83 namespace uim {
84 
85 static int nr_contexts;
86 static struct context_slot_ {
87   client::ClientInterface *session;
88   commands::Output *output;
89   commands::CompositionMode currentMode;
90   bool has_preedit_before;
91   bool need_cand_reactivate;
92   int prev_page;
93   int cand_nr_before;
94   uint64 last_sync_time;
95 #if USE_CASCADING_CANDIDATES
96   vector<int32> *unique_candidate_ids;
97 #endif
98   config::Config::PreeditMethod preedit_method;
99 } *context_slot;
100 
101 static KeyTranslator *keyTranslator;
102 static bool enable_reconversion;
103 static void update_all(uim_lisp mc_, int id);
104 
105 static int
unused_context_id(void)106 unused_context_id(void)
107 {
108   int i;
109 
110   for (i = 0; i < nr_contexts; i++) {
111     if (!context_slot[i].session)
112       return i;
113   }
114 
115   nr_contexts++;
116   context_slot = (context_slot_ *)uim_realloc(context_slot, sizeof(struct context_slot_) * (nr_contexts));
117 
118   return i;
119 }
120 
121 static void
SyncData(int id,bool force)122 SyncData(int id, bool force)
123 {
124   if (context_slot[id].session == NULL)
125     return;
126 
127   const uint64 current_time = GetTime();
128   if (force ||
129       (current_time >= context_slot[id].last_sync_time &&
130        current_time - context_slot[id].last_sync_time >= kSyncDataInterval)) {
131     context_slot[id].session->SyncData();
132     context_slot[id].last_sync_time = current_time;
133   }
134 }
135 
136 static void
update_deletion_range(uim_lisp mc_,int id)137 update_deletion_range(uim_lisp mc_, int id)
138 {
139   commands::Output *output = context_slot[id].output;
140   int offset, length;
141 
142   if (!enable_reconversion)
143     return;
144 
145   if (!output->has_deletion_range())
146     return;
147 
148   offset = output->deletion_range().offset();
149   length = output->deletion_range().length();
150 
151   if (offset + length < 0)
152     return;
153 
154   uim_scm_callf("im-delete-text", "oyyii", mc_, "primary", "cursor", -offset, offset + length);
155 }
156 
157 static void
update_result(uim_lisp mc_,int id)158 update_result(uim_lisp mc_, int id)
159 {
160   commands::Output *output = context_slot[id].output;
161 
162   if (!output->has_result())
163     return;
164 
165   const char *str = output->result().value().c_str();
166   uim_scm_callf("im-commit", "os", mc_, str);
167 }
168 
169 static uim_lisp
insert_cursor(uim_lisp segs,const commands::Preedit::Segment & segment,int attr,int pos)170 insert_cursor(uim_lisp segs, const commands::Preedit::Segment &segment, int attr, int pos)
171 {
172   size_t len = segment.value_length();
173 
174   string former = Util::SubString(segment.value(), 0, pos);
175   string latter = Util::SubString(segment.value(), pos, len);
176 
177   uim_lisp seg_f, seg_c, seg_l;
178   if (pos == 0) {
179     seg_f = uim_scm_null(); /* not used */
180     seg_c = CONS(MAKE_INT(UPreeditAttr_Cursor), MAKE_STR(""));
181     seg_l = CONS(MAKE_INT(attr), MAKE_STR(latter.c_str()));
182 
183     segs = CONS(seg_c, segs);
184     segs = CONS(seg_l, segs);
185   } else {
186     seg_f = CONS(MAKE_INT(attr), MAKE_STR(former.c_str()));
187     seg_c = CONS(MAKE_INT(UPreeditAttr_Cursor), MAKE_STR(""));
188     seg_l = CONS(MAKE_INT(attr), MAKE_STR(latter.c_str()));
189 
190     segs = CONS(seg_f, segs);
191     segs = CONS(seg_c, segs);
192     segs = CONS(seg_l, segs);
193   }
194 
195   return segs;
196 }
197 
198 static uim_lisp
compose_preedit(const commands::Output * output)199 compose_preedit(const commands::Output *output)
200 {
201   const commands::Preedit &preedit = output->preedit();
202   uim_lisp segs = uim_scm_null();
203   uim_lisp separator = uim_scm_callf("mozc-separator", "");
204   int cursorPos;
205   int count = 0;
206   int seg_count = preedit.segment_size();
207 
208   cursorPos = output->preedit().cursor();
209 
210   for (int i = 0; i < seg_count; ++i) {
211     const commands::Preedit::Segment segment = preedit.segment(i);
212     const char *str = segment.value().c_str();
213     int attr;
214     int prev_count = count;
215     uim_lisp seg;
216     count += segment.value_length();
217 
218     switch (segment.annotation()) {
219     case commands::Preedit::Segment::NONE:
220       attr = UPreeditAttr_None;
221       break;
222     case commands::Preedit::Segment::UNDERLINE:
223       attr = UPreeditAttr_UnderLine;
224       break;
225     case commands::Preedit::Segment::HIGHLIGHT:
226       attr = UPreeditAttr_Reverse | UPreeditAttr_Cursor;
227       break;
228     default:
229       attr = UPreeditAttr_None;
230       break;
231     }
232 
233     if (((prev_count < cursorPos) && (count > cursorPos)) || cursorPos == 0) {
234       uim_lisp new_segs;
235       if ((new_segs = insert_cursor(segs, segment, attr, cursorPos - prev_count)) != uim_scm_null()) {
236          segs = new_segs;
237          continue;
238       }
239     }
240 
241     seg = CONS(MAKE_INT(attr), MAKE_STR(str));
242 
243     if (TRUEP(separator) && i > 0)
244       segs = CONS(separator, segs);
245     segs = CONS(seg, segs);
246 
247     if (count == cursorPos && !output->preedit().has_highlighted_position()) {
248       seg = CONS(MAKE_INT(UPreeditAttr_Cursor), MAKE_STR(""));
249       segs = CONS(seg, segs);
250     }
251   }
252 
253   return uim_scm_callf("reverse", "o", segs);
254 }
255 
256 static void
update_preedit(uim_lisp mc_,int id)257 update_preedit(uim_lisp mc_, int id)
258 {
259   uim_lisp preedit;
260   commands::Output *output = context_slot[id].output;
261 
262   if (!output->has_preedit()) {
263     if (context_slot[id].has_preedit_before) {
264       uim_scm_callf("context-update-preedit", "oo", mc_, uim_scm_null());
265     }
266     context_slot[id].has_preedit_before = false;
267 
268     return;
269   } else {
270     preedit = compose_preedit(output);
271     context_slot[id].has_preedit_before = true;
272   }
273   uim_scm_callf("context-update-preedit", "oo", mc_, preedit);
274 }
275 
276 static void
update_candidates(uim_lisp mc_,int id)277 update_candidates(uim_lisp mc_, int id)
278 {
279   commands::Output *output = context_slot[id].output;
280 
281   if (!output->has_candidates()) {
282     uim_scm_callf("im-deactivate-candidate-selector", "o", mc_);
283     context_slot[id].cand_nr_before = 0;
284 
285     return;
286   }
287 
288   const commands::Candidates &candidates = output->candidates();
289   bool first_time = false;
290   bool has_focused_index = candidates.has_focused_index();
291   int current_page = has_focused_index ? candidates.focused_index() / 9 : 0;
292 
293   if ((context_slot[id].cand_nr_before != candidates.size()) || !has_focused_index)
294     first_time = true;
295 
296   if (first_time || (context_slot[id].need_cand_reactivate && current_page != context_slot[id].prev_page)) {
297     uim_scm_callf("im-activate-candidate-selector", "oii", mc_, candidates.size(), 9);
298     // cope with issue #6
299     if (current_page != 0)
300       context_slot[id].need_cand_reactivate = true;
301     else
302       context_slot[id].need_cand_reactivate = false;
303   }
304   context_slot[id].prev_page = current_page;
305 
306   if (has_focused_index) {
307     int index = candidates.focused_index();
308     uim_scm_callf("im-select-candidate", "oi", mc_, index);
309   }
310   context_slot[id].cand_nr_before = candidates.size();
311 
312 #if USE_CASCADING_CANDIDATES
313   if (first_time || (candidates.has_focused_index() && candidates.focused_index() % 9 == 0)) {
314     context_slot[id].unique_candidate_ids->clear();
315     for (int i = 0; i < candidates.candidate_size(); ++i) {
316       if (candidates.candidate(i).has_id()) {
317         const int32 cand_id = candidates.candidate(i).id();
318         context_slot[id].unique_candidate_ids->push_back(cand_id);
319       } else {
320         // The parent node of the cascading window does not have an id since the
321         // node does not contain a candidate word.
322         context_slot[id].unique_candidate_ids->push_back(kBadCandidateId);
323       }
324     }
325   }
326 #endif
327 }
328 
329 static void
update_composition_mode(uim_lisp mc_,int id)330 update_composition_mode(uim_lisp mc_, int id)
331 {
332   commands::Output *output = context_slot[id].output;
333 
334   if (!output->has_mode())
335     return;
336 
337   const commands::CompositionMode newMode = output->mode();
338   if (context_slot[id].currentMode == newMode)
339     return;
340 
341   context_slot[id].currentMode = newMode;
342 }
343 
344 static void
execute_callback(uim_lisp mc_,int id)345 execute_callback(uim_lisp mc_, int id)
346 {
347   commands::Output *output = context_slot[id].output;
348 
349   if (!enable_reconversion)
350     return;
351 
352   if (!output->has_callback())
353     return;
354 
355   if (!output->callback().has_session_command())
356     return;
357 
358   const commands::SessionCommand &command = output->callback().session_command();
359   if (!command.has_type())
360     return;
361 
362   const commands::SessionCommand::CommandType type = command.type();
363   commands::SessionCommand session_command;
364   session_command.set_type(type);
365   int use_primary_text = 0;
366 
367   switch (type) {
368   case commands::SessionCommand::UNDO:
369     // do nothing.
370     break;
371   case commands::SessionCommand::CONVERT_REVERSE:
372     {
373       // try selected text first
374       uim_lisp ustr = uim_scm_callf("im-acquire-text", "oyyiy", mc_, "selection", "beginning", 0, "full");
375       uim_lisp latter;
376 
377       if (TRUEP(ustr) &&
378 	  !NULLP(latter = uim_scm_callf("ustr-latter-seq", "o", ustr))) {
379 	  uim_lisp str = CAR(latter);
380 
381           string text = REFER_C_STR(str);
382           session_command.set_text(text);
383       } else {
384 #if 0
385 	// then primary text
386         uim_lisp former;
387         ustr = uim_scm_callf("im-acquire-text", "oyyyi", mc_, "primary", "cursor", "line", 0);
388 	if (TRUEP(ustr) && !NULLP(former = uim_scm_callf("ustr-former-seq", "o", ustr))) {
389 	  uim_lisp str = CAR(former);
390 	  string text = REFER_C_STR(str);
391 	  session_command.set_text(text);
392 	  use_primary_text = 1;
393 	} else
394 	  return;
395 #else
396         // UNDO if no selection
397         session_command.set_type(commands::SessionCommand::UNDO);
398 #endif
399       }
400     }
401     break;
402   default:
403     return;
404   }
405 
406   if (!context_slot[id].session->SendCommand(session_command, context_slot[id].output)) {
407     // callback command failed
408     return;
409   }
410 
411   if (type == commands::SessionCommand::CONVERT_REVERSE) {
412     if (use_primary_text)
413       uim_scm_callf("im-delete-text", "oyyyi", mc_, "primary", "cursor", "line", 0);
414     else
415       uim_scm_callf("im-delete-text", "oyyiy", mc_, "selection", "beginning", 0, "full");
416   }
417   update_all(mc_, id);
418 }
419 
420 static void
update_all(uim_lisp mc_,int id)421 update_all(uim_lisp mc_, int id)
422 {
423   update_deletion_range(mc_, id);
424   update_result(mc_, id);
425   update_preedit(mc_, id);
426   update_candidates(mc_, id);
427   update_composition_mode(mc_, id);
428   execute_callback(mc_, id);
429 }
430 
431 static uim_lisp
create_context(uim_lisp mc_)432 create_context(uim_lisp mc_)
433 {
434   int id;
435 
436   client::ClientInterface *session = new client::Client;
437   commands::Output *output = new commands::Output;
438   if (!keyTranslator)
439     keyTranslator = new KeyTranslator;
440 
441   id = unused_context_id();
442   context_slot[id].session = session;
443   context_slot[id].output = output;
444   context_slot[id].currentMode = commands::HIRAGANA;
445   context_slot[id].has_preedit_before = false;
446   context_slot[id].need_cand_reactivate = false;
447   context_slot[id].cand_nr_before = 0;
448   context_slot[id].prev_page = 0;
449 #if USE_CASCADING_CANDIDATES
450   context_slot[id].unique_candidate_ids = new vector<int32>;
451 #endif
452 
453   // Launch mozc_server
454   // or should I call this with mozc-on-key?
455   session->EnsureConnection();
456 #if !USE_CASCADING_CANDIDATES
457   session->EnableCascadingWindow(false);
458 #endif
459 
460   if (!enable_reconversion) {
461     if (!FALSEP(uim_scm_callf("symbol-bound?", "y", "mozc-check-uim-version")))
462       enable_reconversion = (bool)C_BOOL(uim_scm_callf("mozc-check-uim-version", "iii", 1, 7, 2));
463   }
464 
465   if (enable_reconversion) {
466     commands::Capability capability;
467     capability.set_text_deletion(commands::Capability::DELETE_PRECEDING_TEXT);
468     session->set_client_capability(capability);
469   }
470 
471 
472   return MAKE_INT(id);
473 }
474 
475 static uim_lisp
release_context(uim_lisp id_)476 release_context(uim_lisp id_)
477 {
478   int id = C_INT(id_);
479 
480   if (id < nr_contexts) {
481     SyncData(id, true);
482     delete context_slot[id].session;
483     delete context_slot[id].output;
484 #if USE_CASCADING_CANDIDATES
485     delete context_slot[id].unique_candidate_ids;
486 #endif
487     context_slot[id].session = NULL;
488     context_slot[id].output = NULL;
489   }
490 
491   return uim_scm_f();
492 }
493 
494 static uim_lisp
reset_context(uim_lisp id_)495 reset_context(uim_lisp id_)
496 {
497   return uim_scm_t();
498 }
499 
500 static uim_lisp
press_key(uim_lisp mc_,uim_lisp id_,uim_lisp key_,uim_lisp state_)501 press_key(uim_lisp mc_, uim_lisp id_, uim_lisp key_, uim_lisp state_)
502 {
503   client::ClientInterface *session;
504   commands::KeyEvent key;
505   int id;
506   int keyval, keycode, modifiers;
507   config::Config::PreeditMethod preedit_method;
508   char *keyboard;
509   bool layout_is_jp;
510 
511   id = C_INT(id_);
512   session = context_slot[id].session;
513   preedit_method = context_slot[id].preedit_method;
514   keyboard = uim_scm_symbol_value_str("mozc-keyboard-type-for-kana-input-method");
515   layout_is_jp = keyboard && !strcmp(keyboard, "jp-keyboard") ? true : false;
516   free(keyboard);
517 
518   keyval = C_INT(key_);
519   modifiers = C_INT(state_);
520   keycode = 0; /* XXX */
521 
522   if (!(*keyTranslator).Translate(keyval, keycode, modifiers, preedit_method, layout_is_jp, &key))
523     return uim_scm_f();
524 
525   if (uim_scm_symbol_value_bool("mozc-use-context-aware-conversion?")) {
526     commands::Context context;
527     uim_lisp ustr = uim_scm_callf("im-acquire-text", "oyyyy", mc_, "primary", "cursor", "line", "line");
528     uim_lisp former, latter, str;
529     if (TRUEP(ustr)) {
530       if(!NULLP(former = uim_scm_callf("ustr-former-seq", "o", ustr))) {
531         str = CAR(former);
532 	context.set_preceding_text(REFER_C_STR(str));
533       }
534       if(!NULLP(latter = uim_scm_callf("ustr-latter-seq", "o", ustr))) {
535         str = CAR(latter);
536 	context.set_following_text(REFER_C_STR(str));
537       }
538     }
539     if (!(*session).SendKeyWithContext(key, context, context_slot[id].output))
540       return uim_scm_f();
541   } else {
542     if (!(*session).SendKey(key, context_slot[id].output))
543       return uim_scm_f();
544   }
545 
546   update_all(mc_, id);
547 
548   const bool consumed = context_slot[id].output->consumed();
549 #if 0
550   fprintf(stderr, "debugstring %s\n", output.DebugString().c_str());
551   fprintf(stderr, "consumed %d\n", consumed ? 1 : 0);
552 #endif
553 
554   return consumed ? uim_scm_t() : uim_scm_f();
555 }
556 
557 static uim_lisp
release_key(uim_lisp id_,uim_lisp key_,uim_lisp state_)558 release_key(uim_lisp id_, uim_lisp key_, uim_lisp state_)
559 {
560   return uim_scm_f();
561 }
562 
563 static uim_lisp
get_nr_candidates(uim_lisp id_)564 get_nr_candidates(uim_lisp id_)
565 {
566   int id = C_INT(id_);
567   commands::Output *output = context_slot[id].output;
568 
569   return MAKE_INT(output->candidates().size());
570 }
571 
572 static uim_lisp
get_nth_candidate(uim_lisp id_,uim_lisp nth_)573 get_nth_candidate(uim_lisp id_, uim_lisp nth_)
574 {
575   int id = C_INT(id_);
576   commands::Output *output = context_slot[id].output;
577   const commands::Candidates &candidates = output->candidates();
578   const char *cand, *prefix, *suffix;
579   char *s;
580 
581   int nth;
582   int idx;
583   int nr;
584   int page_nr;
585 
586   nth = C_INT(nth_);
587   nr = candidates.size();
588   page_nr = candidates.candidate_size();
589 
590   if (nth < nr) {
591     idx = nth % 9;
592 
593     if (idx < page_nr) {
594       prefix = candidates.candidate(idx).annotation().prefix().c_str();
595       cand = candidates.candidate(idx).value().c_str();
596       suffix = candidates.candidate(idx).annotation().suffix().c_str();
597       if (asprintf(&s, "%s%s%s", prefix, cand, suffix) == -1)
598         s = strdup("");
599     } else {
600       s = strdup("");
601     }
602   } else
603     s = strdup("");
604 
605   return MAKE_STR_DIRECTLY(s);
606 }
607 
608 static uim_lisp
get_nth_label(uim_lisp id_,uim_lisp nth_)609 get_nth_label(uim_lisp id_, uim_lisp nth_)
610 {
611   int id = C_INT(id_);
612   commands::Output *output = context_slot[id].output;
613   const commands::Candidates &candidates = output->candidates();
614   const char *label;
615 
616   int nth;
617   int idx;
618   int nr;
619   int page_nr;
620 
621   nth = C_INT(nth_);
622   nr = candidates.size();
623   page_nr = candidates.candidate_size();
624 
625   if (nth < nr) {
626     idx = nth % 9;
627     if (idx < page_nr)
628       label = candidates.candidate(idx).annotation().shortcut().c_str();
629     else
630       label = "";
631   } else
632     label = "";
633 
634   return MAKE_STR(label);
635 }
636 
637 static uim_lisp
get_nth_annotation(uim_lisp id_,uim_lisp nth_)638 get_nth_annotation(uim_lisp id_, uim_lisp nth_)
639 {
640   int id = C_INT(id_);
641   commands::Output *output = context_slot[id].output;
642   const commands::Candidates &candidates = output->candidates();
643   const char *annotation;
644 
645   int nth;
646   int idx;
647   int nr;
648   int page_nr;
649 
650   nth = C_INT(nth_);
651   nr = candidates.size();
652   page_nr = candidates.candidate_size();
653 
654   if (nth < nr) {
655     idx = nth % 9;
656     if (idx < page_nr)
657       annotation = candidates.candidate(idx).annotation().description().c_str();
658     else
659       annotation = "";
660 
661   } else
662     annotation = "";
663 
664   return MAKE_STR(annotation);
665 }
666 
667 /* from uim-key.c */
668 static struct key_entry {
669   int key;
670   const char *str;
671 } key_tab[] = {
672   {UKey_Yen, "yen"},
673   {UKey_Backspace, "backspace"},
674   {UKey_Delete, "delete"},
675   {UKey_Escape, "escape"},
676   {UKey_Return, "return"},
677   {UKey_Tab, "tab"},
678   {UKey_Left, "left"},
679   {UKey_Up, "up"},
680   {UKey_Right, "right"},
681   {UKey_Down, "down"},
682   {UKey_Prior, "prior"},
683   {UKey_Next, "next"},
684   {UKey_Home, "home"},
685   {UKey_End, "end"},
686   {UKey_Insert, "insert"},
687   {UKey_Multi_key, "Multi_key"},
688   {UKey_Codeinput, "codeinput"},
689   {UKey_SingleCandidate, "single-candidate"},
690   {UKey_MultipleCandidate, "multiple-candidate"},
691   {UKey_PreviousCandidate, "previous-candidate"},
692   {UKey_Mode_switch, "Mode_switch"},
693   {UKey_Kanji, "Kanji"},
694   {UKey_Muhenkan, "Muhenkan"},
695   {UKey_Henkan_Mode, "Henkan_Mode"},
696   {UKey_Romaji, "romaji"},
697   {UKey_Hiragana, "hiragana"},
698   {UKey_Katakana, "katakana"},
699   {UKey_Hiragana_Katakana, "hiragana-katakana"},
700   {UKey_Zenkaku, "zenkaku"},
701   {UKey_Hankaku, "hankaku"},
702   {UKey_Zenkaku_Hankaku, "zenkaku-hankaku"},
703   {UKey_Touroku, "touroku"},
704   {UKey_Massyo, "massyo"},
705   {UKey_Kana_Lock, "kana-lock"},
706   {UKey_Kana_Shift, "kana-shift"},
707   {UKey_Eisu_Shift, "eisu-shift"},
708   {UKey_Eisu_toggle, "eisu-toggle"},
709 
710   {UKey_Hangul, "hangul"},
711   {UKey_Hangul_Start, "hangul-start"},
712   {UKey_Hangul_End, "hangul-end"},
713   {UKey_Hangul_Hanja, "hangul-hanja"},
714   {UKey_Hangul_Jamo, "hangul-jamo"},
715   {UKey_Hangul_Romaja, "hangul-romaja"},
716   {UKey_Hangul_Codeinput, "hangul-codeinput"},
717   {UKey_Hangul_Jeonja, "hangul-jeonja"},
718   {UKey_Hangul_Banja, "hangul-banja"},
719   {UKey_Hangul_PreHanja, "hangul-prehanja"},
720   {UKey_Hangul_PostHanja, "hangul-posthanja"},
721   {UKey_Hangul_SingleCandidate, "hangul-single-candidate"},
722   {UKey_Hangul_MultipleCandidate, "hangul-multiple-candidate"},
723   {UKey_Hangul_PreviousCandidate, "hangul-previous-candidate"},
724   {UKey_Hangul_Special, "hangul-special"},
725 
726   {UKey_F1, "F1"},
727   {UKey_F2, "F2"},
728   {UKey_F3, "F3"},
729   {UKey_F4, "F4"},
730   {UKey_F5, "F5"},
731   {UKey_F6, "F6"},
732   {UKey_F7, "F7"},
733   {UKey_F8, "F8"},
734   {UKey_F9, "F9"},
735   {UKey_F10, "F10"},
736   {UKey_F11, "F11"},
737   {UKey_F12, "F12"},
738   {UKey_F13, "F13"},
739   {UKey_F14, "F14"},
740   {UKey_F15, "F15"},
741   {UKey_F16, "F16"},
742   {UKey_F17, "F17"},
743   {UKey_F18, "F18"},
744   {UKey_F19, "F19"},
745   {UKey_F20, "F20"},
746   {UKey_F21, "F21"},
747   {UKey_F22, "F22"},
748   {UKey_F23, "F23"},
749   {UKey_F24, "F24"},
750   {UKey_F25, "F25"},
751   {UKey_F26, "F26"},
752   {UKey_F27, "F27"},
753   {UKey_F28, "F28"},
754   {UKey_F29, "F29"},
755   {UKey_F30, "F30"},
756   {UKey_F31, "F31"},
757   {UKey_F32, "F32"},
758   {UKey_F33, "F33"},
759   {UKey_F34, "F34"},
760   {UKey_F35, "F35"},
761 
762   {UKey_Dead_Grave, "dead-grave"},
763   {UKey_Dead_Acute, "dead-acute"},
764   {UKey_Dead_Circumflex, "dead-circumflex"},
765   {UKey_Dead_Tilde, "dead-tilde"},
766   {UKey_Dead_Macron, "dead-macron"},
767   {UKey_Dead_Breve, "dead-breve"},
768   {UKey_Dead_Abovedot, "dead-abovedot"},
769   {UKey_Dead_Diaeresis, "dead-diaeresis"},
770   {UKey_Dead_Abovering, "dead-abovering"},
771   {UKey_Dead_Doubleacute, "dead-doubleacute"},
772   {UKey_Dead_Caron, "dead-caron"},
773   {UKey_Dead_Cedilla, "dead-cedilla"},
774   {UKey_Dead_Ogonek, "dead-ogonek"},
775   {UKey_Dead_Iota, "dead-iota"},
776   {UKey_Dead_VoicedSound, "dead-voiced-sound"},
777   {UKey_Dead_SemivoicedSound, "dead-semivoiced-sound"},
778   {UKey_Dead_Belowdot, "dead-belowdot"},
779   {UKey_Dead_Hook, "dead-hook"},
780   {UKey_Dead_Horn, "dead-horn"},
781 
782   {UKey_Kana_Fullstop, "kana-fullstop"},
783   {UKey_Kana_OpeningBracket, "kana-opening-bracket"},
784   {UKey_Kana_ClosingBracket, "kana-closing-bracket"},
785   {UKey_Kana_Comma, "kana-comma"},
786   {UKey_Kana_Conjunctive, "kana-conjunctive"},
787   {UKey_Kana_WO, "kana-WO"},
788   {UKey_Kana_a, "kana-a"},
789   {UKey_Kana_i, "kana-i"},
790   {UKey_Kana_u, "kana-u"},
791   {UKey_Kana_e, "kana-e"},
792   {UKey_Kana_o, "kana-o"},
793   {UKey_Kana_ya, "kana-ya"},
794   {UKey_Kana_yu, "kana-yu"},
795   {UKey_Kana_yo, "kana-yo"},
796   {UKey_Kana_tsu, "kana-tsu"},
797   {UKey_Kana_ProlongedSound, "kana-prolonged-sound"},
798   {UKey_Kana_A, "kana-A"},
799   {UKey_Kana_I, "kana-I"},
800   {UKey_Kana_U, "kana-U"},
801   {UKey_Kana_E, "kana-E"},
802   {UKey_Kana_O, "kana-O"},
803   {UKey_Kana_KA, "kana-KA"},
804   {UKey_Kana_KI, "kana-KI"},
805   {UKey_Kana_KU, "kana-KU"},
806   {UKey_Kana_KE, "kana-KE"},
807   {UKey_Kana_KO, "kana-KO"},
808   {UKey_Kana_SA, "kana-SA"},
809   {UKey_Kana_SHI, "kana-SHI"},
810   {UKey_Kana_SU, "kana-SU"},
811   {UKey_Kana_SE, "kana-SE"},
812   {UKey_Kana_SO, "kana-SO"},
813   {UKey_Kana_TA, "kana-TA"},
814   {UKey_Kana_CHI, "kana-CHI"},
815   {UKey_Kana_TSU, "kana-TSU"},
816   {UKey_Kana_TE, "kana-TE"},
817   {UKey_Kana_TO, "kana-TO"},
818   {UKey_Kana_NA, "kana-NA"},
819   {UKey_Kana_NI, "kana-NI"},
820   {UKey_Kana_NU, "kana-NU"},
821   {UKey_Kana_NE, "kana-NE"},
822   {UKey_Kana_NO, "kana-NO"},
823   {UKey_Kana_HA, "kana-HA"},
824   {UKey_Kana_HI, "kana-HI"},
825   {UKey_Kana_FU, "kana-FU"},
826   {UKey_Kana_HE, "kana-HE"},
827   {UKey_Kana_HO, "kana-HO"},
828   {UKey_Kana_MA, "kana-MA"},
829   {UKey_Kana_MI, "kana-MI"},
830   {UKey_Kana_MU, "kana-MU"},
831   {UKey_Kana_ME, "kana-ME"},
832   {UKey_Kana_MO, "kana-MO"},
833   {UKey_Kana_YA, "kana-YA"},
834   {UKey_Kana_YU, "kana-YU"},
835   {UKey_Kana_YO, "kana-YO"},
836   {UKey_Kana_RA, "kana-RA"},
837   {UKey_Kana_RI, "kana-RI"},
838   {UKey_Kana_RU, "kana-RU"},
839   {UKey_Kana_RE, "kana-RE"},
840   {UKey_Kana_RO, "kana-RO"},
841   {UKey_Kana_WA, "kana-WA"},
842   {UKey_Kana_N, "kana-N"},
843   {UKey_Kana_VoicedSound, "kana-voiced-sound"},
844   {UKey_Kana_SemivoicedSound, "kana-semivoiced-sound"},
845 
846   {UKey_Private1, "Private1"},
847   {UKey_Private2, "Private2"},
848   {UKey_Private3, "Private3"},
849   {UKey_Private4, "Private4"},
850   {UKey_Private5, "Private5"},
851   {UKey_Private6, "Private6"},
852   {UKey_Private7, "Private7"},
853   {UKey_Private8, "Private8"},
854   {UKey_Private9, "Private9"},
855   {UKey_Private10, "Private10"},
856   {UKey_Private11, "Private11"},
857   {UKey_Private12, "Private12"},
858   {UKey_Private13, "Private13"},
859   {UKey_Private14, "Private14"},
860   {UKey_Private15, "Private15"},
861   {UKey_Private16, "Private16"},
862   {UKey_Private17, "Private17"},
863   {UKey_Private18, "Private18"},
864   {UKey_Private19, "Private19"},
865   {UKey_Private20, "Private20"},
866   {UKey_Private21, "Private21"},
867   {UKey_Private22, "Private22"},
868   {UKey_Private23, "Private23"},
869   {UKey_Private24, "Private24"},
870   {UKey_Private25, "Private25"},
871   {UKey_Private26, "Private26"},
872   {UKey_Private27, "Private27"},
873   {UKey_Private28, "Private28"},
874   {UKey_Private29, "Private29"},
875   {UKey_Private30, "Private30"},
876   {UKey_Shift_key, "Shift_key"},
877   {UKey_Alt_key, "Alt_key"},
878   {UKey_Control_key, "Control_key"},
879   {UKey_Meta_key, "Meta_key"},
880   {UKey_Super_key, "Super_key"},
881   {UKey_Hyper_key, "Hyper_key"},
882 
883   {UKey_Caps_Lock, "caps-lock"},
884   {UKey_Num_Lock, "num-lock"},
885   {UKey_Scroll_Lock, "scroll-lock"},
886   /*  {UKey_Other, "other"},*/
887   {0, 0}
888 };
889 
890 struct eqstr
891 {
operator ()mozc::uim::eqstr892   bool operator()(const char* s1, const char* s2) const
893   {
894     return strcmp(s1, s2) == 0;
895   }
896 };
897 
898 typedef hash_map<const char *, int, __gnu_cxx::hash<const char *>, eqstr> KeyMap;
899 static KeyMap key_map;
900 
install_keymap(void)901 static void install_keymap(void)
902 {
903   int i;
904 
905   for (i = 0; key_tab[i].key; i++)
906     key_map.insert(std::make_pair(key_tab[i].str, key_tab[i].key));
907 }
908 
909 static uim_lisp
keysym_to_int(uim_lisp sym_)910 keysym_to_int(uim_lisp sym_)
911 {
912   const char *sym = uim_scm_refer_c_str(sym_);
913   int key = 0;
914 
915   KeyMap::iterator it = key_map.find(sym);
916   if (it != key_map.end())
917     key = it->second;
918 
919   return uim_scm_make_int(key);
920 }
921 
922 static uim_lisp
get_composition_mode(uim_lisp id_)923 get_composition_mode(uim_lisp id_)
924 {
925   int id = C_INT(id_);
926   const commands::CompositionMode mode = context_slot[id].currentMode;
927   int type = 0;
928 
929   switch (mode) {
930   case commands::DIRECT:
931     type = -1;
932     break;
933   case commands::HIRAGANA:
934     type = 0;
935     break;
936   case commands::FULL_KATAKANA:
937     type = 1;
938     break;
939   case commands::HALF_KATAKANA:
940     type = 2;
941     break;
942   case commands::HALF_ASCII:
943     type = 3;
944     break;
945   case commands::FULL_ASCII:
946     type = 4;
947     break;
948   default:
949     type = -1;
950     break;
951   }
952 
953   return MAKE_INT(type);
954 }
955 
956 static uim_lisp
set_composition_mode(uim_lisp mc_,uim_lisp id_,uim_lisp new_mode_)957 set_composition_mode(uim_lisp mc_, uim_lisp id_, uim_lisp new_mode_)
958 {
959   int id = C_INT(id_);
960   commands::CompositionMode mode;
961   commands::SessionCommand command;
962 
963   switch (C_INT(new_mode_)) {
964   case -1:
965     mode = commands::DIRECT;
966     break;
967   case 0:
968     mode = commands::HIRAGANA;
969     break;
970   case 1:
971     mode = commands::FULL_KATAKANA;
972     break;
973   case 2:
974     mode = commands::HALF_KATAKANA;
975     break;
976   case 3:
977     mode = commands::HALF_ASCII;
978     break;
979   case 4:
980     mode = commands::FULL_ASCII;
981     break;
982   default:
983     mode = commands::HIRAGANA;
984     break;
985   }
986 
987   if (mode == commands::DIRECT) {
988     command.set_type(commands::SessionCommand::SUBMIT);
989     context_slot[id].session->SendCommand(command, context_slot[id].output);
990     update_all(mc_, id);
991     uim_scm_callf("mozc-context-set-on!", "oo", mc_, uim_scm_f());
992   } else {
993     command.set_type(commands::SessionCommand::SWITCH_INPUT_MODE);
994     command.set_composition_mode(mode);
995     context_slot[id].session->SendCommand(command, context_slot[id].output);
996     context_slot[id].currentMode = mode; /* don't set this with DIRECT mode */
997     uim_scm_callf("mozc-context-set-on!", "oo", mc_, uim_scm_t());
998   }
999 
1000   return uim_scm_t();
1001 }
1002 
1003 static uim_lisp
set_composition_on(uim_lisp id_)1004 set_composition_on(uim_lisp id_)
1005 {
1006   int id = C_INT(id_);
1007   commands::SessionCommand command;
1008 
1009   command.set_type(commands::SessionCommand::SWITCH_INPUT_MODE);
1010   command.set_composition_mode(context_slot[id].currentMode);
1011   context_slot[id].session->SendCommand(command, context_slot[id].output);
1012 
1013   return uim_scm_t();
1014 }
1015 
1016 static uim_lisp
has_preedit(uim_lisp id_)1017 has_preedit(uim_lisp id_)
1018 {
1019   int id = C_INT(id_);
1020 
1021   return context_slot[id].has_preedit_before ? uim_scm_t() : uim_scm_f();
1022 }
1023 
1024 static uim_lisp
select_candidate(uim_lisp mc_,uim_lisp id_,uim_lisp idx_)1025 select_candidate(uim_lisp mc_, uim_lisp id_, uim_lisp idx_)
1026 {
1027   int id = C_INT(id_);
1028   int idx = C_INT(idx_) % 9;
1029 
1030 #if USE_CASCADING_CANDIDATES
1031   if (idx >= context_slot[id].unique_candidate_ids->size())
1032 #else
1033   if (idx >= context_slot[id].output->candidates().candidate_size())
1034 #endif
1035     return uim_scm_f();
1036 
1037 #if USE_CASCADING_CANDIDATES
1038   const int32 cand_id = (*context_slot[id].unique_candidate_ids)[idx];
1039   if (cand_id == kBadCandidateId)
1040     return uim_scm_f();
1041 #else
1042   const int32 cand_id = context_slot[id].output->candidates().candidate(idx).id();
1043 #endif
1044 
1045   commands::SessionCommand command;
1046   command.set_type(commands::SessionCommand::SELECT_CANDIDATE);
1047   command.set_id(cand_id);
1048   context_slot[id].session->SendCommand(command, context_slot[id].output);
1049   update_all(mc_, id);
1050 
1051   return uim_scm_t();
1052 }
1053 
1054 static uim_lisp
get_input_rule(uim_lisp id_)1055 get_input_rule(uim_lisp id_)
1056 {
1057   int id = C_INT(id_);
1058   const config::Config::PreeditMethod method = context_slot[id].preedit_method;
1059   int rule = 0;
1060 
1061   switch (method) {
1062   case config::Config::ROMAN:
1063     rule = 0;
1064     break;
1065   case config::Config::KANA:
1066     rule = 1;
1067     break;
1068   default:
1069     rule = 0;
1070     break;
1071   }
1072 
1073   return MAKE_INT(rule);
1074 }
1075 
1076 static uim_lisp
set_input_rule(uim_lisp mc_,uim_lisp id_,uim_lisp new_rule_)1077 set_input_rule(uim_lisp mc_, uim_lisp id_, uim_lisp new_rule_)
1078 {
1079   int id = C_INT(id_);
1080   config::Config config;
1081   config::Config::PreeditMethod method;
1082 
1083   switch (C_INT(new_rule_)) {
1084   case 0:
1085     method = config::Config::ROMAN;
1086     break;
1087   case 1:
1088     method = config::Config::KANA;
1089     break;
1090   default:
1091     method = config::Config::ROMAN;
1092     break;
1093   }
1094 
1095   if (!context_slot[id].session->GetConfig(&config))
1096     return uim_scm_f();
1097 
1098   config.set_preedit_method(method);
1099 
1100   if (!context_slot[id].session->SetConfig(config))
1101     return uim_scm_f();
1102 
1103   context_slot[id].preedit_method = method;
1104 
1105   return uim_scm_t();
1106 }
1107 
1108 static uim_lisp
reconvert(uim_lisp mc_,uim_lisp id_)1109 reconvert(uim_lisp mc_, uim_lisp id_)
1110 {
1111   if (!enable_reconversion)
1112     return uim_scm_f();
1113 
1114   int id = C_INT(id_);
1115   commands::SessionCommand session_command;
1116   session_command.set_type(commands::SessionCommand::CONVERT_REVERSE);
1117 
1118   // try selected text first, then primary text
1119   uim_lisp ustr = uim_scm_callf("im-acquire-text", "oyyiy", mc_, "selection" , "beginning", 0, "full");
1120   uim_lisp former, latter;
1121   int use_primary_text = 0;
1122 
1123   if (TRUEP(ustr) &&
1124       !NULLP(latter = uim_scm_callf("ustr-latter-seq", "o", ustr))) {
1125     uim_lisp str = CAR(latter);
1126 
1127     string text = REFER_C_STR(str);
1128     session_command.set_text(text);
1129   } else {
1130     ustr = uim_scm_callf("im-acquire-text", "oyyyi", mc_, "primary", "cursor", "line", 0);
1131     if (TRUEP(ustr) &&
1132 	!NULLP(former = uim_scm_callf("ustr-former-seq", "o", ustr))) {
1133       uim_lisp str = CAR(former);
1134       string text = REFER_C_STR(str);
1135       session_command.set_text(text);
1136       use_primary_text = 1;
1137     } else
1138       return uim_scm_f();
1139   }
1140 
1141   if (!context_slot[id].session->SendCommand(session_command, context_slot[id].output))
1142     return uim_scm_f();
1143 
1144   if (use_primary_text)
1145     uim_scm_callf("im-delete-text", "oyyyi", mc_, "primary", "cursor", "line", 0);
1146   else
1147     uim_scm_callf("im-delete-text", "oyyiy", mc_, "selection", "beginning", 0, "full");
1148   update_all(mc_, id);
1149 
1150   return uim_scm_t();
1151 }
1152 
1153 static uim_lisp
submit(uim_lisp mc_,uim_lisp id_)1154 submit(uim_lisp mc_, uim_lisp id_)
1155 {
1156   int id = C_INT(id_);
1157   commands::SessionCommand command;
1158 
1159   command.set_type(commands::SessionCommand::SUBMIT);
1160   context_slot[id].session->SendCommand(command, context_slot[id].output);
1161   update_all(mc_, id);
1162 
1163   return uim_scm_t();
1164 }
1165 
1166 } // namespace
1167 } // namespace
1168 
1169 
1170 
1171 void
uim_plugin_instance_init(void)1172 uim_plugin_instance_init(void)
1173 {
1174   uim_scm_init_proc1("mozc-lib-alloc-context", mozc::uim::create_context);
1175   uim_scm_init_proc1("mozc-lib-free-context", mozc::uim::release_context);
1176   uim_scm_init_proc1("mozc-lib-reset", mozc::uim::reset_context);
1177   uim_scm_init_proc4("mozc-lib-press-key", mozc::uim::press_key);
1178   uim_scm_init_proc3("mozc-lib-release-key", mozc::uim::release_key);
1179   uim_scm_init_proc1("mozc-lib-get-nr-candidates", mozc::uim::get_nr_candidates);
1180   uim_scm_init_proc2("mozc-lib-get-nth-candidate", mozc::uim::get_nth_candidate);
1181   uim_scm_init_proc2("mozc-lib-get-nth-label", mozc::uim::get_nth_label);
1182   uim_scm_init_proc2("mozc-lib-get-nth-annotation", mozc::uim::get_nth_annotation);
1183   uim_scm_init_proc1("keysym-to-int", mozc::uim::keysym_to_int);
1184   uim_scm_init_proc1("mozc-lib-input-mode", mozc::uim::get_composition_mode);
1185   uim_scm_init_proc3("mozc-lib-set-input-mode", mozc::uim::set_composition_mode);
1186   uim_scm_init_proc1("mozc-lib-set-on", mozc::uim::set_composition_on);
1187   uim_scm_init_proc1("mozc-lib-has-preedit?", mozc::uim::has_preedit);
1188   uim_scm_init_proc3("mozc-lib-set-candidate-index", mozc::uim::select_candidate);
1189   uim_scm_init_proc1("mozc-lib-input-rule", mozc::uim::get_input_rule);
1190   uim_scm_init_proc3("mozc-lib-set-input-rule", mozc::uim::set_input_rule);
1191   uim_scm_init_proc2("mozc-lib-reconvert", mozc::uim::reconvert);
1192   uim_scm_init_proc2("mozc-lib-submit-composition", mozc::uim::submit);
1193 
1194   int argc = 1;
1195   static const char name[] = "uim-mozc";
1196   argv = (char **)malloc(sizeof(char *) * 2);
1197   argv[0] = (char *)name;
1198   argv[1] =  NULL;
1199 
1200   mozc::InitMozc((const char *)argv[0], &argc, (char ***)&argv, true);
1201   mozc::uim::install_keymap();
1202 }
1203 
1204 void
uim_plugin_instance_quit(void)1205 uim_plugin_instance_quit(void)
1206 {
1207   mozc::uim::key_map.clear();
1208   for (int i = 0; i < mozc::uim::nr_contexts; i++) {
1209     if (mozc::uim::context_slot[i].session) {
1210       delete mozc::uim::context_slot[i].session;
1211       delete mozc::uim::context_slot[i].output;
1212     }
1213   }
1214   delete mozc::uim::keyTranslator;
1215   mozc::uim::keyTranslator = NULL;
1216   free(argv);
1217 }
1218