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