1 /***************************************************************************
2 * Copyright (C) 2002~2005 by Yuking *
3 * yuking_net@sohu.com *
4 * *
5 * This program is free software; you can redistribute it and/or modify *
6 * it under the terms of the GNU General Public License as published by *
7 * the Free Software Foundation; either version 2 of the License, or *
8 * (at your option) any later version. *
9 * *
10 * This program is distributed in the hope that it will be useful, *
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
13 * GNU General Public License for more details. *
14 * *
15 * You should have received a copy of the GNU General Public License *
16 * along with this program; if not, write to the *
17 * Free Software Foundation, Inc., *
18 * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. *
19 ***************************************************************************/
20
21 /**
22 * @file ime.c
23 * @author Yuking yuking_net@sohu.com
24 * @date 2008-1-16
25 *
26 * Process Keyboard Event and Input Method
27 *
28 */
29 #include <dlfcn.h>
30 #include <libintl.h>
31 #include <time.h>
32 #include "config.h"
33 #include "ime.h"
34 #include "addon.h"
35 #include "fcitx-config/xdg.h"
36 #include "fcitx-utils/log.h"
37 #include "fcitx-config/hotkey.h"
38 #include "fcitx/configfile.h"
39 #include "fcitx/keys.h"
40 #include "fcitx/profile.h"
41 #include "ime-internal.h"
42 #include "ui.h"
43 #include "fcitx-utils/utils.h"
44 #include "hook.h"
45 #include "frontend.h"
46 #include "hook-internal.h"
47 #include "instance.h"
48 #include "module.h"
49 #include "candidate.h"
50 #include "instance-internal.h"
51 #include "fcitx-internal.h"
52 #include "addon-internal.h"
53 #include "context-internal.h"
54
55
56 static const FcitxHotkey* switchKey1[] = {
57 FCITX_RCTRL,
58 FCITX_RSHIFT,
59 FCITX_LSHIFT,
60 FCITX_LCTRL,
61 FCITX_ALT_LSHIFT,
62 FCITX_ALT_RSHIFT,
63
64 FCITX_RCTRL,
65 FCITX_RSHIFT,
66
67 FCITX_LALT,
68 FCITX_RALT,
69 FCITX_LALT,
70
71 FCITX_LSUPER,
72 FCITX_RSUPER,
73 FCITX_LSUPER,
74
75 FCITX_CTRL_LSUPER,
76 FCITX_CTRL_RSUPER,
77 FCITX_SUPER_LCTRL,
78 FCITX_SUPER_RCTRL,
79 FCITX_NONE_KEY,
80 };
81
82 static const FcitxHotkey* switchKey2[] = {
83 FCITX_NONE_KEY,
84 FCITX_NONE_KEY,
85 FCITX_NONE_KEY,
86 FCITX_NONE_KEY,
87 FCITX_NONE_KEY,
88 FCITX_NONE_KEY,
89
90 FCITX_LCTRL,
91 FCITX_LSHIFT,
92
93 FCITX_NONE_KEY,
94 FCITX_NONE_KEY,
95 FCITX_RALT,
96
97 FCITX_NONE_KEY,
98 FCITX_NONE_KEY,
99 FCITX_RSUPER,
100
101 FCITX_NONE_KEY,
102 FCITX_NONE_KEY,
103 FCITX_NONE_KEY,
104 FCITX_NONE_KEY,
105 FCITX_NONE_KEY
106 };
107
108 static const FcitxHotkey* imSWNextKey1[] = {
109 FCITX_LCTRL_LSHIFT,
110 FCITX_LALT_LSHIFT,
111 FCITX_LCTRL_LSUPER,
112 FCITX_LALT_LSUPER,
113 };
114
115 static const FcitxHotkey* imSWNextKey2[] = {
116 FCITX_LCTRL_LSHIFT2,
117 FCITX_LALT_LSHIFT2,
118 FCITX_LCTRL_LSUPER2,
119 FCITX_LALT_LSUPER2,
120 };
121
122 static const FcitxHotkey* imSWPrevKey1[] = {
123 FCITX_RCTRL_RSHIFT,
124 FCITX_RALT_RSHIFT,
125 FCITX_RCTRL_RSUPER,
126 FCITX_RALT_RSUPER,
127 };
128
129 static const FcitxHotkey* imSWPrevKey2[] = {
130 FCITX_RCTRL_RSHIFT2,
131 FCITX_RALT_RSHIFT2,
132 FCITX_RCTRL_RSUPER2,
133 FCITX_RALT_RSUPER2,
134 };
135
136
137 static const UT_icd ime_icd = { sizeof(FcitxIM), NULL , NULL, NULL };
138 static boolean IMMenuAction(FcitxUIMenu* menu, int index);
139 static void UpdateIMMenuItem(FcitxUIMenu *menu);
140 static void FcitxInstanceEnableIMInternal(FcitxInstance* instance, FcitxInputContext* ic, boolean keepState);
141 static void FcitxInstanceCloseIMInternal(FcitxInstance* instance, FcitxInputContext* ic);
142 static void FcitxInstanceChangeIMStateWithKey(FcitxInstance* instance, FcitxInputContext* ic, boolean withSwitchKey);
143 static void FcitxInstanceChangeIMStateInternal(FcitxInstance* instance, FcitxInputContext* ic, FcitxContextState objectState, boolean withSwitchKey);
144 static void FreeIMEntry(FcitxIMEntry* entry);
145
FCITX_GETTER_VALUE(FcitxInputState,IsInRemind,bIsInRemind,boolean)146 FCITX_GETTER_VALUE(FcitxInputState, IsInRemind, bIsInRemind, boolean)
147 FCITX_SETTER(FcitxInputState, IsInRemind, bIsInRemind, boolean)
148 FCITX_GETTER_VALUE(FcitxInputState, IsDoInputOnly, bIsDoInputOnly, boolean)
149 FCITX_SETTER(FcitxInputState, IsDoInputOnly, bIsDoInputOnly, boolean)
150 FCITX_GETTER_VALUE(FcitxInputState, OutputString, strStringGet, char*)
151 FCITX_GETTER_VALUE(FcitxInputState, LastCommitString, strLastCommit, const char*)
152 FCITX_GETTER_VALUE(FcitxInputState, RawInputBuffer, strCodeInput, char*)
153 FCITX_GETTER_VALUE(FcitxInputState, CursorPos, iCursorPos, int)
154 FCITX_SETTER(FcitxInputState, CursorPos, iCursorPos, int)
155 FCITX_GETTER_VALUE(FcitxInputState, ClientCursorPos, iClientCursorPos, int)
156 FCITX_SETTER(FcitxInputState, ClientCursorPos, iClientCursorPos, int)
157 FCITX_GETTER_VALUE(FcitxInputState, CandidateList, candList, struct _FcitxCandidateWordList*)
158 FCITX_GETTER_VALUE(FcitxInputState, AuxUp, msgAuxUp, FcitxMessages*)
159 FCITX_GETTER_VALUE(FcitxInputState, AuxDown, msgAuxDown, FcitxMessages*)
160 FCITX_GETTER_VALUE(FcitxInputState, Preedit, msgPreedit, FcitxMessages*)
161 FCITX_GETTER_VALUE(FcitxInputState, ClientPreedit, msgClientPreedit, FcitxMessages*)
162 FCITX_GETTER_VALUE(FcitxInputState, RawInputBufferSize, iCodeInputCount, int)
163 FCITX_SETTER(FcitxInputState, RawInputBufferSize, iCodeInputCount, int)
164 FCITX_GETTER_VALUE(FcitxInputState, ShowCursor, bShowCursor, boolean)
165 FCITX_SETTER(FcitxInputState, ShowCursor, bShowCursor, boolean)
166 FCITX_GETTER_VALUE(FcitxInputState, LastIsSingleChar, lastIsSingleHZ, boolean)
167 FCITX_SETTER(FcitxInputState, KeyCode, keycode, uint32_t)
168 FCITX_SETTER(FcitxInputState, KeySym, keysym, uint32_t)
169 FCITX_SETTER(FcitxInputState, KeyState, keystate, uint32_t)
170 FCITX_GETTER_VALUE(FcitxInputState, KeyCode, keycode, uint32_t)
171 FCITX_GETTER_VALUE(FcitxInputState, KeySym, keysym, uint32_t)
172 FCITX_GETTER_VALUE(FcitxInputState, KeyState, keystate, uint32_t)
173 FCITX_SETTER(FcitxInputState, LastIsSingleChar, lastIsSingleHZ, boolean)
174
175 CONFIG_BINDING_BEGIN(FcitxIMEntry)
176 CONFIG_BINDING_REGISTER("InputMethod", "UniqueName", uniqueName)
177 CONFIG_BINDING_REGISTER("InputMethod", "Name", name)
178 CONFIG_BINDING_REGISTER("InputMethod", "IconName", iconName)
179 CONFIG_BINDING_REGISTER("InputMethod", "Parent", parent)
180 CONFIG_BINDING_REGISTER("InputMethod", "LangCode", langCode)
181 CONFIG_BINDING_REGISTER("InputMethod", "Priority", priority)
182 CONFIG_BINDING_END()
183
184 FcitxInputState* FcitxInputStateCreate()
185 {
186 FcitxInputState* input = fcitx_utils_malloc0(sizeof(FcitxInputState));
187
188 input->msgAuxUp = FcitxMessagesNew();
189 input->msgAuxDown = FcitxMessagesNew();
190 input->msgPreedit = FcitxMessagesNew();
191 input->msgClientPreedit = FcitxMessagesNew();
192 input->candList = FcitxCandidateWordNewList();
193
194 return input;
195 }
196
IMPriorityCmp(const void * a,const void * b)197 int IMPriorityCmp(const void *a, const void *b)
198 {
199 FcitxIM *ta, *tb;
200 ta = (FcitxIM*)a;
201 tb = (FcitxIM*)b;
202 int delta = ta->iPriority - tb->iPriority;
203 if (delta != 0)
204 return delta;
205 else
206 return strcmp(ta->uniqueName, tb->uniqueName);
207 }
208
FcitxInstanceInitBuiltInHotkey(FcitxInstance * instance)209 void FcitxInstanceInitBuiltInHotkey(FcitxInstance *instance)
210 {
211 FcitxHotkeyHook hk;
212 hk.hotkey = instance->config->hkReloadConfig;
213 hk.hotkeyhandle = ImProcessReload;
214 hk.arg = instance;
215 FcitxInstanceRegisterHotkeyFilter(instance, hk);
216
217 hk.hotkey = FCITX_ENTER;
218 hk.hotkeyhandle = ImProcessEnter;
219 hk.arg = instance;
220 FcitxInstanceRegisterHotkeyFilter(instance, hk);
221
222 hk.hotkey = FCITX_ESCAPE;
223 hk.hotkeyhandle = ImProcessEscape;
224 hk.arg = instance;
225 FcitxInstanceRegisterHotkeyFilter(instance, hk);
226
227 hk.hotkey = instance->config->hkRemind;
228 hk.hotkeyhandle = ImProcessRemind;
229 hk.arg = instance;
230 FcitxInstanceRegisterHotkeyFilter(instance, hk);
231
232 hk.hotkey = instance->config->hkSaveAll;
233 hk.hotkeyhandle = ImProcessSaveAll;
234 hk.arg = instance;
235 FcitxInstanceRegisterHotkeyFilter(instance, hk);
236
237 hk.hotkey = instance->config->hkSwitchEmbeddedPreedit;
238 hk.hotkeyhandle = ImSwitchEmbeddedPreedit;
239 hk.arg = instance;
240 FcitxInstanceRegisterHotkeyFilter(instance, hk);
241 }
242
FcitxInstanceInitIM(FcitxInstance * instance)243 void FcitxInstanceInitIM(FcitxInstance* instance)
244 {
245 utarray_init(&instance->imes, &ime_icd);
246 utarray_init(&instance->availimes, &ime_icd);
247 utarray_init(&instance->imeclasses, fcitx_ptr_icd);
248 }
249
250 FCITX_EXPORT_API
FcitxInstanceSaveAllIM(FcitxInstance * instance)251 void FcitxInstanceSaveAllIM(FcitxInstance* instance)
252 {
253 UT_array* imes = &instance->availimes;
254 FcitxIM *pim;
255 for (pim = (FcitxIM*)utarray_front(imes);
256 pim != NULL;
257 pim = (FcitxIM*)utarray_next(imes, pim)) {
258 if (pim->Save) {
259 pim->Save(pim->klass);
260 }
261 }
262 }
263
FcitxInstanceCheckICFromSameApplication(FcitxInstance * instance,FcitxInputContext * rec,FcitxInputContext * ic)264 boolean FcitxInstanceCheckICFromSameApplication (FcitxInstance* instance, FcitxInputContext* rec, FcitxInputContext* ic) {
265 if (rec->frontendid != ic->frontendid)
266 return false;
267 if (rec == ic)
268 return true;
269 FcitxInputContext2* rec2 = (FcitxInputContext2*) rec;
270 FcitxInputContext2* ic2 = (FcitxInputContext2*) ic;
271 if (rec2->imname || ic2->imname)
272 return false;
273 FcitxAddon **pfrontend = FcitxInstanceGetPFrontend(instance, ic->frontendid);
274 if (pfrontend) {
275 FcitxFrontend *frontend = (*pfrontend)->frontend;
276 if (frontend->CheckICFromSameApplication &&
277 frontend->CheckICFromSameApplication((*pfrontend)->addonInstance,
278 rec, ic)) {
279 return true;
280 }
281 }
282
283 return false;
284 }
285
FcitxInstanceLoadIM(FcitxInstance * instance,FcitxAddon * addon)286 void FcitxInstanceLoadIM(FcitxInstance* instance, FcitxAddon* addon)
287 {
288 if (!addon)
289 return;
290
291 if (addon->type == AT_SHAREDLIBRARY) {
292 char* modulePath;
293 FILE *fp = FcitxXDGGetLibFile(addon->library, "r", &modulePath);
294 void *handle;
295 FcitxIMClass2 * imclass2;
296 if (!fp) {
297 free(modulePath);
298 return;
299 }
300 fclose(fp);
301 handle = dlopen(modulePath, RTLD_NOW | RTLD_NODELETE | (addon->loadLocal ? RTLD_LOCAL : RTLD_GLOBAL));
302 if (!handle) {
303 FcitxLog(ERROR, _("IM: open %s fail %s") , modulePath , dlerror());
304 free(modulePath);
305 return;
306 }
307 free(modulePath);
308
309 if (!FcitxCheckABIVersion(handle, addon->name)) {
310 FcitxLog(ERROR, "%s ABI Version Error", addon->name);
311 dlclose(handle);
312 return;
313 }
314
315 boolean isIMClass2 = false;
316 imclass2 = FcitxGetSymbol(handle, addon->name, "ime2");
317 if (!imclass2)
318 imclass2 = FcitxGetSymbol(handle, addon->name, "ime");
319 else
320 isIMClass2 = true;
321 if (!imclass2 || !imclass2->Create) {
322 FcitxLog(ERROR, _("IM: bad im %s"), addon->name);
323 dlclose(handle);
324 return;
325 }
326
327 addon->isIMClass2 = isIMClass2;
328 instance->currentIMAddon = addon;
329 if ((addon->addonInstance = imclass2->Create(instance)) == NULL) {
330 dlclose(handle);
331 return;
332 }
333 instance->currentIMAddon = NULL;
334 addon->imclass2 = imclass2;
335 utarray_push_back(&instance->imeclasses, &addon);
336 }
337 }
338
FcitxInstanceRegisterEmptyIMEntry(FcitxInstance * instance,const char * name,const char * uniqueName,const char * iconName,int priority,const char * langCode,FcitxAddon * addon)339 void FcitxInstanceRegisterEmptyIMEntry(FcitxInstance *instance,
340 const char* name,
341 const char* uniqueName,
342 const char* iconName,
343 int priority,
344 const char *langCode,
345 FcitxAddon* addon
346 )
347 {
348 UT_array* imes = &instance->availimes ;
349 FcitxIM* entry = FcitxInstanceGetIMFromIMList(instance, IMAS_Disable, uniqueName);
350 if (entry) {
351 return;
352 } else {
353 utarray_extend_back(imes);
354 entry = (FcitxIM*) utarray_back(imes);
355 }
356
357 if (entry == NULL)
358 return;
359
360 entry->uniqueName = strdup(uniqueName);
361 entry->strName = strdup(name);
362 entry->strIconName = strdup(iconName);
363 entry->iPriority = priority;
364 strncpy(entry->langCode, langCode, LANGCODE_LENGTH);
365 entry->langCode[LANGCODE_LENGTH] = 0;
366 entry->initialized = false;
367 entry->owner = addon;
368 }
369
370 FCITX_EXPORT_API
FcitxInstanceRegisterIM(FcitxInstance * instance,void * imclass,const char * uniqueName,const char * name,const char * iconName,FcitxIMInit Init,FcitxIMResetIM ResetIM,FcitxIMDoInput DoInput,FcitxIMGetCandWords GetCandWords,FcitxIMPhraseTips PhraseTips,FcitxIMSave Save,FcitxIMReloadConfig ReloadConfig,FcitxIMKeyBlocker KeyBlocker,int priority,const char * langCode)371 void FcitxInstanceRegisterIM(FcitxInstance *instance,
372 void *imclass,
373 const char* uniqueName,
374 const char* name,
375 const char* iconName,
376 FcitxIMInit Init,
377 FcitxIMResetIM ResetIM,
378 FcitxIMDoInput DoInput,
379 FcitxIMGetCandWords GetCandWords,
380 FcitxIMPhraseTips PhraseTips,
381 FcitxIMSave Save,
382 FcitxIMReloadConfig ReloadConfig,
383 FcitxIMKeyBlocker KeyBlocker,
384 int priority,
385 const char *langCode
386 )
387 {
388 FcitxIMIFace iface;
389 memset(&iface, 0, sizeof(FcitxIMIFace));
390 iface.Init = Init;
391 iface.ResetIM = ResetIM;
392 iface.DoInput = DoInput;
393 iface.GetCandWords = GetCandWords;
394 iface.PhraseTips = PhraseTips;
395 iface.Save = Save;
396 iface.ReloadConfig = ReloadConfig;
397 iface.KeyBlocker = KeyBlocker;
398 FcitxInstanceRegisterIMv2(instance,
399 imclass,
400 uniqueName,
401 name,
402 iconName,
403 iface,
404 priority,
405 langCode
406 );
407 }
408
409 FCITX_EXPORT_API
FcitxInstanceRegisterIMv2(FcitxInstance * instance,void * imclass,const char * uniqueName,const char * name,const char * iconName,FcitxIMIFace iface,int priority,const char * langCode)410 void FcitxInstanceRegisterIMv2(FcitxInstance *instance,
411 void *imclass,
412 const char* uniqueName,
413 const char* name,
414 const char* iconName,
415 FcitxIMIFace iface,
416 int priority,
417 const char *langCode
418 )
419 {
420 if (priority <= 0)
421 return ;
422
423 if (priority == PRIORITY_MAGIC_FIRST)
424 priority = 0;
425
426 UT_array* imes = &instance->availimes ;
427 FcitxIM* entry;
428
429 entry = FcitxInstanceGetIMFromIMList(instance, IMAS_Disable, uniqueName);
430 if (entry) {
431 if (entry->initialized) {
432 FcitxLog(ERROR, "%s already exists", uniqueName);
433 return ;
434 }
435 } else {
436 utarray_extend_back(imes);
437 entry = (FcitxIM*) utarray_back(imes);
438 }
439
440 if (entry == NULL)
441 return;
442
443 if (!entry->uniqueName)
444 entry->uniqueName = strdup(uniqueName);
445 if (!entry->strName)
446 entry->strName = strdup(name);
447 if (!entry->strIconName)
448 entry->strIconName = strdup(iconName);
449 entry->Init = iface.Init;
450 entry->ResetIM = iface.ResetIM;
451 entry->DoInput = iface.DoInput;
452 entry->GetCandWords = iface.GetCandWords;
453 entry->PhraseTips = iface.PhraseTips;
454 entry->Save = iface.Save;
455 entry->ReloadConfig = iface.ReloadConfig;
456 entry->KeyBlocker = iface.KeyBlocker;
457 entry->UpdateSurroundingText = iface.UpdateSurroundingText;
458 entry->DoReleaseInput = iface.DoReleaseInput;
459 entry->OnClose = iface.OnClose;
460 entry->GetSubModeName = iface.GetSubModeName;
461 entry->klass = imclass;
462 entry->iPriority = priority;
463 if (langCode)
464 strncpy(entry->langCode, langCode, LANGCODE_LENGTH);
465 entry->langCode[LANGCODE_LENGTH] = 0;
466 entry->initialized = true;
467 entry->owner = instance->currentIMAddon;
468 }
469
470 CONFIG_DESC_DEFINE(GetIMConfigDesc, "inputmethod.desc")
471
FcitxInstanceLoadAllIM(FcitxInstance * instance)472 boolean FcitxInstanceLoadAllIM(FcitxInstance* instance)
473 {
474 FcitxStringHashSet* sset = FcitxXDGGetFiles("inputmethod", NULL, ".conf");
475 FcitxStringHashSet* curs = sset;
476 UT_array* addons = &instance->addons;
477
478 while (curs) {
479 FILE* fp = FcitxXDGGetFileWithPrefix("inputmethod", curs->name, "r", NULL);
480 FcitxConfigFile* cfile = NULL;
481 if (fp) {
482 cfile = FcitxConfigParseConfigFileFp(fp, GetIMConfigDesc());
483 fclose(fp);
484 }
485
486 if (cfile) {
487 FcitxIMEntry* entry = fcitx_utils_malloc0(sizeof(FcitxIMEntry));
488 FcitxIMEntryConfigBind(entry, cfile, GetIMConfigDesc());
489 FcitxConfigBindSync(&entry->config);
490 FcitxAddon *addon = FcitxAddonsGetAddonByName(&instance->addons, entry->parent);
491
492 if (addon
493 && addon->bEnabled
494 && addon->category == AC_INPUTMETHOD
495 && addon->registerMethod == IMRM_CONFIGFILE
496 ) {
497 FcitxInstanceRegisterEmptyIMEntry(instance,
498 entry->name,
499 entry->uniqueName,
500 entry->iconName,
501 entry->priority,
502 entry->langCode,
503 addon
504 );
505 }
506 FreeIMEntry(entry);
507 }
508 curs = curs->hh.next;
509 }
510
511 fcitx_utils_free_string_hash_set(sset);
512
513 /* only load those input methods that can only register by themselves */
514 FcitxAddon *addon;
515 for (addon = (FcitxAddon *) utarray_front(addons);
516 addon != NULL;
517 addon = (FcitxAddon *) utarray_next(addons, addon)) {
518 /*
519 * since we want to use this function to do some "reload"
520 * we need to check addon->addonInstance
521 */
522 if (addon->bEnabled && addon->category == AC_INPUTMETHOD && !addon->addonInstance) {
523 switch (addon->type) {
524 case AT_SHAREDLIBRARY: {
525 if (addon->registerMethod == IMRM_SELF)
526 FcitxInstanceLoadIM(instance, addon);
527 }
528 break;
529 default:
530 break;
531 }
532 }
533 }
534
535 if (utarray_len(&instance->availimes) <= 0) {
536 FcitxLog(ERROR, _("No available Input Method"));
537 return false;
538 }
539
540 instance->imLoaded = true;
541
542 FcitxInstanceUpdateIMList(instance);
543
544 return true;
545 }
546
547 static void
_NormalizeHotkeyForModifier(const FcitxHotkey * origin,FcitxHotkey * group1,FcitxHotkey * group2)548 _NormalizeHotkeyForModifier(const FcitxHotkey* origin, FcitxHotkey* group1, FcitxHotkey* group2)
549 {
550 int i;
551 memcpy(group1, origin, sizeof(FcitxHotkey[2]));
552 for (i = 0; i < 2; i ++) {
553 if (FcitxHotkeyIsHotKeyModifierCombine(group1[i].sym, group1[i].state)) {
554 group1[i].state &= ~FcitxHotkeyModifierToState(group1[i].sym);
555 }
556 }
557 memcpy(group2, origin, sizeof(FcitxHotkey[2]));
558 for (i = 0; i < 2; i ++) {
559 if (FcitxHotkeyIsHotKeyModifierCombine(group2[i].sym, group2[i].state)) {
560 group2[i].state |= FcitxHotkeyModifierToState(group2[i].sym);
561 }
562 }
563 }
564
565 /**
566 * consider different situation
567 * ctrl+space -> on press (reason space is not modifier
568 * space -> on press
569 * a -> on press
570 * tab -> on press
571 * ctrl -> on release
572 *
573 * once here's a wrong implementation that using all key without unicode as trigger on release
574 * which passes press to im. (to mozc)
575 */
IsTriggerOnRelease(FcitxKeySym sym,unsigned int state)576 static inline boolean IsTriggerOnRelease(FcitxKeySym sym, unsigned int state) {
577 return (FcitxHotkeyIsHotKeyModifierCombine(sym, state));
578 }
579
_DoTrigger(FcitxInstance * instance)580 INPUT_RETURN_VALUE _DoTrigger(FcitxInstance* instance)
581 {
582 if (FcitxInstanceGetCurrentState(instance) == IS_INACTIVE) {
583 FcitxInstanceChangeIMState(instance, instance->CurrentIC);
584 FcitxInstanceShowInputSpeed(instance, false);
585 } else {
586 FcitxInstanceCloseIM(instance, instance->CurrentIC);
587 }
588 return IRV_DO_NOTHING;
589 }
590
_DoActivate(FcitxInstance * instance)591 INPUT_RETURN_VALUE _DoActivate(FcitxInstance* instance)
592 {
593 if (FcitxInstanceGetCurrentState(instance) != IS_ACTIVE) {
594 FcitxInstanceEnableIM(instance, instance->CurrentIC, false);
595 return IRV_DO_NOTHING;
596 }
597 return IRV_TO_PROCESS;
598 }
599
_DoDeactivate(FcitxInstance * instance)600 INPUT_RETURN_VALUE _DoDeactivate(FcitxInstance* instance)
601 {
602 if (FcitxInstanceGetCurrentState(instance) == IS_ACTIVE) {
603 FcitxInstanceCloseIM(instance, instance->CurrentIC);
604 return IRV_DO_NOTHING;
605 }
606 return IRV_TO_PROCESS;
607 }
608
_DoSwitchIM(FcitxInstance * instance)609 INPUT_RETURN_VALUE _DoSwitchIM(FcitxInstance* instance)
610 {
611 if (instance->config->bIMSwitchKey
612 && (instance->config->bIMSwitchIncludeInactive || FcitxInstanceGetCurrentState(instance) == IS_ACTIVE)) {
613 FcitxInstanceSwitchIMByIndex(instance, instance->config->bIMSwitchIncludeInactive ? -1 : -3);
614 return IRV_DO_NOTHING;
615 }
616 return IRV_TO_PROCESS;
617 }
618
_DoSwitchIMReverse(FcitxInstance * instance)619 INPUT_RETURN_VALUE _DoSwitchIMReverse(FcitxInstance* instance)
620 {
621 if (instance->config->bIMSwitchKey
622 && (instance->config->bIMSwitchIncludeInactive || FcitxInstanceGetCurrentState(instance) == IS_ACTIVE)) {
623 FcitxInstanceSwitchIMByIndex(instance, instance->config->bIMSwitchIncludeInactive ? -2 : -4);
624 return IRV_DO_NOTHING;
625 }
626 return IRV_TO_PROCESS;
627 }
628
_DoSwitch(FcitxInstance * instance)629 INPUT_RETURN_VALUE _DoSwitch(FcitxInstance* instance)
630 {
631 INPUT_RETURN_VALUE retVal = IRV_DONOT_PROCESS;
632 FcitxIM* currentIM = FcitxInstanceGetCurrentIM(instance);
633 if (currentIM && currentIM->OnClose) {
634 currentIM->OnClose(currentIM->klass, CET_ChangeByInactivate);
635 }
636 else {
637 if (instance->config->bSendTextWhenSwitchEng) {
638 if (instance->input->iCodeInputCount != 0) {
639 strcpy(FcitxInputStateGetOutputString(instance->input), FcitxInputStateGetRawInputBuffer(instance->input));
640 retVal = IRV_ENG;
641 }
642 }
643 }
644 FcitxInstanceChangeIMStateWithKey(instance, instance->CurrentIC, true);
645 FcitxInstanceShowInputSpeed(instance, false);
646
647 return retVal;
648 }
649
_Do2ndSelect(FcitxInstance * instance)650 INPUT_RETURN_VALUE _Do2ndSelect(FcitxInstance* instance)
651 {
652 FcitxInputState* input = instance->input;
653 if (input->bIsInRemind || FcitxCandidateWordGetPageSize(input->candList) != 0) {
654 if (!input->bIsInRemind) {
655 return FcitxCandidateWordChooseByIndex(input->candList, 1);
656 } else {
657 strcpy(FcitxInputStateGetOutputString(input), " ");
658 return IRV_COMMIT_STRING;
659 }
660 }
661 return IRV_TO_PROCESS;
662 }
663
_Do3ndSelect(FcitxInstance * instance)664 INPUT_RETURN_VALUE _Do3ndSelect(FcitxInstance* instance)
665 {
666 FcitxInputState* input = instance->input;
667 if (input->bIsInRemind || FcitxCandidateWordGetPageSize(input->candList) != 0) {
668 if (!input->bIsInRemind) {
669 return FcitxCandidateWordChooseByIndex(input->candList, 2);
670 } else {
671 strcpy(FcitxInputStateGetOutputString(input), "\xe3\x80\x80");
672 return IRV_COMMIT_STRING;
673 }
674 }
675 return IRV_TO_PROCESS;
676 }
677
_Check2ndSelect(FcitxInstance * instance)678 boolean _Check2ndSelect(FcitxInstance* instance) {
679 return FcitxCandidateWordGetByIndex(instance->input->candList, 1) != NULL;
680 }
681
_Check3ndSelect(FcitxInstance * instance)682 boolean _Check3ndSelect(FcitxInstance* instance) {
683 return FcitxCandidateWordGetByIndex(instance->input->candList, 2) != NULL;
684 }
685
_CheckActivate(FcitxInstance * instance)686 boolean _CheckActivate(FcitxInstance* instance)
687 {
688 return FcitxInstanceGetCurrentState(instance) != IS_ACTIVE;
689 }
690
_CheckDeactivate(FcitxInstance * instance)691 boolean _CheckDeactivate(FcitxInstance* instance)
692 {
693 return FcitxInstanceGetCurrentState(instance) == IS_ACTIVE;
694 }
695
_CheckSwitch(FcitxInstance * instance)696 boolean _CheckSwitch(FcitxInstance* instance) {
697 return instance->CurrentIC->state == IS_ACTIVE
698 || !instance->config->bUseExtraTriggerKeyOnlyWhenUseItToInactivate
699 || ((FcitxInputContext2*)(instance->CurrentIC))->switchBySwitchKey;
700 }
701
_CheckSwitchIM(FcitxInstance * instance)702 boolean _CheckSwitchIM(FcitxInstance* instance) {
703 return instance->config->bIMSwitchKey;
704 }
705
706 FCITX_EXPORT_API
FcitxInstanceProcessKey(FcitxInstance * instance,FcitxKeyEventType event,long unsigned int timestamp,FcitxKeySym sym,unsigned int state)707 INPUT_RETURN_VALUE FcitxInstanceProcessKey(
708 FcitxInstance* instance,
709 FcitxKeyEventType event,
710 long unsigned int timestamp,
711 FcitxKeySym sym,
712 unsigned int state)
713 {
714 if (sym == 0) {
715 return IRV_DONOT_PROCESS;
716 }
717
718 INPUT_RETURN_VALUE retVal = IRV_TO_PROCESS;
719 FcitxIM* currentIM = FcitxInstanceGetCurrentIM(instance);
720 FcitxInputState *input = instance->input;
721
722 FcitxGlobalConfig *fc = instance->config;
723
724 const FcitxHotkey* hkSwitchNext1 = imSWNextKey1[fc->iIMSwitchKey];
725 const FcitxHotkey* hkSwitchNext2 = imSWNextKey2[fc->iIMSwitchKey];
726
727 const FcitxHotkey* hkSwitchPrev1 = imSWPrevKey1[fc->iIMSwitchKey];
728 const FcitxHotkey* hkSwitchPrev2 = imSWPrevKey2[fc->iIMSwitchKey];
729
730 const FcitxHotkey* hkSwitchKey1;
731 const FcitxHotkey* hkSwitchKey2;
732
733 FcitxHotkey hkCustomSwitchKey1[2];
734 FcitxHotkey hkCustomSwitchKey2[2];
735 FcitxHotkey hkTrigger1[2];
736 FcitxHotkey hkTrigger2[2];
737 FcitxHotkey hkActivate1[2];
738 FcitxHotkey hkActivate2[2];
739 FcitxHotkey hkInactivate1[2];
740 FcitxHotkey hkInactivate2[2];
741 // check config.desc
742 #define CUSTOM_SWITCH_KEY 19
743 if ((int) fc->iSwitchKey < CUSTOM_SWITCH_KEY) {
744 hkSwitchKey1 = switchKey1[fc->iSwitchKey];
745 hkSwitchKey2 = switchKey2[fc->iSwitchKey];
746 } else {
747 _NormalizeHotkeyForModifier(fc->hkCustomSwitchKey, hkCustomSwitchKey1, hkCustomSwitchKey2);
748 hkSwitchKey1 = hkCustomSwitchKey1;
749 hkSwitchKey2 = hkCustomSwitchKey2;
750 }
751 _NormalizeHotkeyForModifier(fc->hkTrigger, hkTrigger1, hkTrigger2);
752 _NormalizeHotkeyForModifier(fc->hkActivate, hkActivate1, hkActivate2);
753 _NormalizeHotkeyForModifier(fc->hkInactivate, hkInactivate1, hkInactivate2);
754
755 struct {
756 KEY_RELEASED kr;
757 const FcitxHotkey* hk1;
758 const FcitxHotkey* hk2;
759 INPUT_RETURN_VALUE (*callback)(FcitxInstance*);
760 boolean (*check)(FcitxInstance*);
761 } keyHandle[] = {
762 {KR_2ND_SELECTKEY, fc->i2ndSelectKey, NULL, _Do2ndSelect, _Check2ndSelect}, // KR_2ND_SELECTKEY,
763 {KR_3RD_SELECTKEY, fc->i3rdSelectKey, NULL, _Do3ndSelect, _Check3ndSelect}, // KR_3RD_SELECTKEY,
764 {KR_SWITCH, hkSwitchKey1, hkSwitchKey2, _DoSwitch, _CheckSwitch}, // KR_SWITCH
765 {KR_SWITCH_IM, hkSwitchNext1, hkSwitchNext2, _DoSwitchIM, _CheckSwitchIM}, // KR_SWITCH_IM,
766 {KR_SWITCH_IM_REVERSE, hkSwitchPrev1, hkSwitchPrev2, _DoSwitchIMReverse, _CheckSwitchIM}, // KR_SWITCH_IM_REVERSE,
767 {KR_TRIGGER, hkTrigger1, hkTrigger2, _DoTrigger, NULL},
768 {KR_ACTIVATE, hkActivate1, hkActivate2, _DoActivate, _CheckActivate},
769 {KR_DEACTIVATE, hkInactivate1, hkInactivate2, _DoDeactivate, _CheckDeactivate}, // KR_DEACTIVATE
770 };
771
772 if (instance->CurrentIC == NULL)
773 return IRV_TO_PROCESS;
774
775 if (event == FCITX_PRESS_KEY
776 && !FcitxHotkeyIsHotKeyModifierCombine(sym, state))
777 {
778 if (FcitxInstanceRemoveTimeoutByFunc(instance, HideInputSpeed))
779 HideInputSpeed(instance);
780 }
781
782 if (instance->CurrentIC->contextCaps & CAPACITY_PASSWORD)
783 return IRV_TO_PROCESS;
784
785 if (currentIM == NULL)
786 return IRV_TO_PROCESS;
787
788 boolean triggerOnRelease = IsTriggerOnRelease(sym, state);
789
790 #define HAVE_IM (utarray_len(&instance->imes) > 1)
791 #define CHECK_HOTKEY(name) ((name##1 ? FcitxHotkeyIsHotKey(sym, state, name##1) : false) || (name##2 ? FcitxHotkeyIsHotKey(sym, state, name##2) : false))
792
793 /*
794 * for following reason, we cannot just process switch key, 2nd, 3rd key as other simple hotkey
795 * because ctrl, shift, alt are compose key, so hotkey like ctrl+a will also produce a key
796 * release event for ctrl key, so we must make sure the key release right now is the key just
797 * pressed.
798 */
799
800 /* process keyrelease event for switch key and 2nd, 3rd key */
801 if (event == FCITX_RELEASE_KEY
802 && FcitxInstanceGetCurrentState(instance) != IS_CLOSED
803 && (timestamp - input->lastKeyPressedTime) < 500
804 && (!input->bIsDoInputOnly)
805 && HAVE_IM) {
806 int i;
807 for (i = 0; i < FCITX_ARRAY_SIZE(keyHandle); i ++) {
808 if (retVal == IRV_TO_PROCESS
809 && input->keyReleased == keyHandle[i].kr
810 && CHECK_HOTKEY(keyHandle[i].hk)) {
811 if (triggerOnRelease) {
812 retVal = keyHandle[i].callback(instance);
813 } else {
814 retVal = IRV_DO_NOTHING;
815 }
816 }
817
818 if (retVal != IRV_TO_PROCESS) {
819 input->keyReleased = KR_OTHER;
820 break;
821 }
822 }
823 if (i == FCITX_ARRAY_SIZE(keyHandle)) {
824 input->keyReleased = KR_OTHER;
825 }
826 }
827
828 #if 0
829 /* Added by hubert_star AT forum.ubuntu.com.cn */
830 if (event == FCITX_RELEASE_KEY && IsHotKeySimple(sym, state) &&
831 retVal == IRV_TO_PROCESS)
832 return IRV_DO_NOTHING;
833 #endif
834
835 if (retVal == IRV_TO_PROCESS) {
836 /* process key event for switch key */
837 if (event == FCITX_PRESS_KEY) {
838 input->lastKeyPressedTime = timestamp;
839 do {
840 if (!HAVE_IM) {
841 break;
842 }
843 int i;
844 for (i = 0; i < FCITX_ARRAY_SIZE(keyHandle); i ++) {
845 if (CHECK_HOTKEY(keyHandle[i].hk) && (!keyHandle[i].check || keyHandle[i].check(instance))) {
846 if (!triggerOnRelease) {
847 retVal = keyHandle[i].callback(instance);
848 }
849 input->keyReleased = keyHandle[i].kr;
850 break;
851 }
852 }
853 if (i == FCITX_ARRAY_SIZE(keyHandle)) {
854 input->keyReleased = KR_OTHER;
855 }
856 } while(0);
857 }
858 }
859
860 if (retVal == IRV_TO_PROCESS && event == FCITX_RELEASE_KEY) {
861 FcitxInstanceProcessPreReleaseInputFilter(instance, sym, state, &retVal);
862
863 if (retVal == IRV_TO_PROCESS && currentIM && currentIM->DoReleaseInput) {
864 retVal = currentIM->DoReleaseInput(currentIM->klass, sym, state);
865 }
866
867 if (retVal == IRV_TO_PROCESS) {
868 FcitxInstanceProcessPostReleaseInputFilter(instance, sym, state, &retVal);
869 }
870
871 if (retVal == IRV_TO_PROCESS) {
872 retVal = IRV_DONOT_PROCESS;
873 }
874 }
875
876 /* the key processed before this phase is very important,
877 * we don't let any interrupt */
878 if (FcitxInstanceGetCurrentStatev2(instance) == IS_ACTIVE &&
879 retVal == IRV_TO_PROCESS) {
880 if (!input->bIsDoInputOnly) {
881 FcitxInstanceProcessPreInputFilter(instance, sym, state, &retVal);
882 }
883
884 if (retVal == IRV_TO_PROCESS) {
885 if (!FcitxHotkeyIsHotKey(sym, state, imSWNextKey1[fc->iIMSwitchKey]) && currentIM) {
886 retVal = currentIM->DoInput(currentIM->klass, sym, state);
887 }
888 }
889
890 /* check choose key first, because it might trigger update candidates */
891 if (!input->bIsDoInputOnly && retVal == IRV_TO_PROCESS) {
892 int index = FcitxCandidateWordCheckChooseKey(input->candList,
893 sym, state);
894 if (index >= 0)
895 retVal = FcitxCandidateWordChooseByIndex(input->candList,
896 index);
897 }
898 }
899
900 return FcitxInstanceDoInputCallback(instance, retVal, event, timestamp,
901 sym, state);
902 }
903
904
905 FCITX_EXPORT_API
FcitxInstanceChooseCandidateByIndex(FcitxInstance * instance,int index)906 void FcitxInstanceChooseCandidateByIndex(
907 FcitxInstance* instance,
908 int index)
909 {
910 if (!(FcitxInstanceGetCurrentStatev2(instance) == IS_ACTIVE && index < 10)) {
911 return;
912 }
913 FcitxInputState *input = instance->input;
914 INPUT_RETURN_VALUE retVal = FcitxCandidateWordChooseByIndex(input->candList, index);
915 FcitxIM* currentIM = FcitxInstanceGetCurrentIM(instance);
916 if (FcitxInstanceGetCurrentStatev2(instance) == IS_ACTIVE && currentIM && (retVal & IRV_FLAG_UPDATE_CANDIDATE_WORDS)) {
917 if (currentIM->GetCandWords) {
918 FcitxInstanceCleanInputWindow(instance);
919 retVal = currentIM->GetCandWords(currentIM->klass);
920 FcitxInstanceProcessUpdateCandidates(instance);
921 }
922 }
923 FcitxInstanceProcessInputReturnValue(instance, retVal);
924 }
925
926
927 FCITX_EXPORT_API
FcitxInstanceDoInputCallback(FcitxInstance * instance,INPUT_RETURN_VALUE retVal,FcitxKeyEventType event,long unsigned int timestamp,FcitxKeySym sym,unsigned int state)928 INPUT_RETURN_VALUE FcitxInstanceDoInputCallback(
929 FcitxInstance* instance,
930 INPUT_RETURN_VALUE retVal,
931 FcitxKeyEventType event,
932 long unsigned int timestamp,
933 FcitxKeySym sym,
934 unsigned int state)
935 {
936 FCITX_UNUSED(event);
937 FCITX_UNUSED(timestamp);
938 FcitxIM* currentIM = FcitxInstanceGetCurrentIM(instance);
939 FcitxInputState *input = instance->input;
940
941 FcitxGlobalConfig *fc = instance->config;
942
943 if (FcitxInstanceGetCurrentStatev2(instance) == IS_ACTIVE && currentIM &&
944 (retVal & IRV_FLAG_UPDATE_CANDIDATE_WORDS)) {
945 if (currentIM->GetCandWords) {
946 FcitxInstanceCleanInputWindow(instance);
947 retVal = currentIM->GetCandWords(currentIM->klass);
948 FcitxInstanceProcessUpdateCandidates(instance);
949 }
950 }
951
952 /*
953 * since all candidate word are cached in candList, so we don't need to trigger
954 * GetCandWords after go for another page, simply update input window is ok.
955 */
956 if (FcitxInstanceGetCurrentStatev2(instance) == IS_ACTIVE &&
957 !input->bIsDoInputOnly && retVal == IRV_TO_PROCESS) {
958 if (FcitxHotkeyIsHotKey(sym, state,
959 FcitxConfigPrevPageKey(instance, fc))) {
960 if (FcitxCandidateWordGoPrevPage(input->candList))
961 retVal = IRV_DISPLAY_CANDWORDS;
962 } else if (FcitxHotkeyIsHotKey(sym, state,
963 FcitxConfigNextPageKey(instance, fc))) {
964 if (FcitxCandidateWordGoNextPage(input->candList))
965 retVal = IRV_DISPLAY_CANDWORDS;
966 }
967 }
968
969 if (FcitxInstanceGetCurrentStatev2(instance) == IS_ACTIVE &&
970 !input->bIsDoInputOnly && retVal == IRV_TO_PROCESS) {
971 FcitxInstanceProcessPostInputFilter(instance, sym, state, &retVal);
972 }
973
974 if (retVal == IRV_TO_PROCESS) {
975 retVal = FcitxInstanceProcessHotkey(instance, sym, state);
976 }
977
978 if (FcitxInstanceGetCurrentStatev2(instance) == IS_ACTIVE &&
979 !input->bIsDoInputOnly && retVal == IRV_TO_PROCESS) {
980 if (currentIM && currentIM->KeyBlocker)
981 retVal = currentIM->KeyBlocker(currentIM->klass, sym, state);
982 else
983 retVal = FcitxStandardKeyBlocker(input, sym, state);
984 }
985
986 FcitxInstanceProcessInputReturnValue(instance, retVal);
987
988 return retVal;
989 }
990
991 FCITX_EXPORT_API
FcitxInstanceProcessInputReturnValue(FcitxInstance * instance,INPUT_RETURN_VALUE retVal)992 void FcitxInstanceProcessInputReturnValue(
993 FcitxInstance* instance,
994 INPUT_RETURN_VALUE retVal
995 )
996 {
997 FcitxIM* currentIM = FcitxInstanceGetCurrentIM(instance);
998 FcitxInputState *input = instance->input;
999 FcitxGlobalConfig *fc = instance->config;
1000
1001 if (retVal & IRV_FLAG_PENDING_COMMIT_STRING) {
1002 FcitxInstanceCommitString(instance, instance->CurrentIC, FcitxInputStateGetOutputString(input));
1003 }
1004
1005 if (retVal & IRV_FLAG_DO_PHRASE_TIPS) {
1006 FcitxInstanceCleanInputWindow(instance);
1007 if (fc->bPhraseTips && currentIM && currentIM->PhraseTips)
1008 FcitxInstanceDoPhraseTips(instance);
1009 FcitxUIUpdateInputWindow(instance);
1010
1011 FcitxInstanceResetInput(instance);
1012 input->lastIsSingleHZ = 0;
1013 }
1014
1015 if (retVal & IRV_FLAG_RESET_INPUT) {
1016 FcitxInstanceResetInput(instance);
1017 FcitxUICloseInputWindow(instance);
1018 }
1019
1020 if (retVal & IRV_FLAG_DISPLAY_LAST) {
1021 FcitxInstanceCleanInputWindow(instance);
1022 char str[2] = {FcitxInputStateGetRawInputBuffer(input)[0], '\0'};
1023 FcitxMessagesAddMessageStringsAtLast(input->msgAuxUp, MSG_INPUT, str);
1024 FcitxMessagesAddMessageStringsAtLast(
1025 input->msgAuxDown, MSG_TIPS,
1026 FcitxInputStateGetLastCommitString(input));
1027 }
1028
1029 if (retVal & IRV_FLAG_UPDATE_INPUT_WINDOW)
1030 FcitxUIUpdateInputWindow(instance);
1031 }
1032
1033 FCITX_EXPORT_API
FcitxInstanceForwardKey(FcitxInstance * instance,FcitxInputContext * ic,FcitxKeyEventType event,FcitxKeySym sym,unsigned int state)1034 void FcitxInstanceForwardKey(FcitxInstance* instance, FcitxInputContext *ic, FcitxKeyEventType event, FcitxKeySym sym, unsigned int state)
1035 {
1036 if (ic == NULL)
1037 return;
1038 FcitxAddon **pfrontend = FcitxInstanceGetPFrontend(instance, ic->frontendid);
1039 if (pfrontend == NULL)
1040 return;
1041 FcitxFrontend *frontend = (*pfrontend)->frontend;
1042 frontend->ForwardKey((*pfrontend)->addonInstance, ic, event, sym, state);
1043 }
1044
1045 FCITX_EXPORT_API
FcitxInstanceSwitchIM(FcitxInstance * instance,int index)1046 void FcitxInstanceSwitchIM(FcitxInstance* instance, int index)
1047 {
1048 FcitxInstanceSwitchIMByIndex(instance, index);
1049 }
1050
1051 FCITX_EXPORT_API
FcitxInstanceSwitchIMByName(FcitxInstance * instance,const char * name)1052 void FcitxInstanceSwitchIMByName(FcitxInstance* instance, const char* name)
1053 {
1054 FcitxIM* im = FcitxInstanceGetIMFromIMList(instance, IMAS_Enable, name);
1055 if (im) {
1056 if (FcitxInstanceGetCurrentIC(instance)) {
1057 // Ignore the request if current IM is already same with the name
1058 // Add such check here is mainly for GitHub Issue #203.
1059 // Another possibility is to add it to FcitxInstanceSwitchIMInternal,
1060 // but that part of code is too hack and some initialization may
1061 // relies on it.
1062 // This function is only used in UI and DBus remote control,
1063 // Thus it is safe to change it here.
1064 FcitxIM* currentIM = FcitxInstanceGetCurrentIM(instance);
1065 if (currentIM && strcmp(currentIM->strName, name) == 0) {
1066 return;
1067 }
1068
1069 int idx = FcitxInstanceGetIMIndexByName(instance, name);
1070 if (idx < 0)
1071 return;
1072 FcitxInstanceSwitchIMByIndex(instance, idx);
1073 } else {
1074 FcitxInstanceSetDelayedIM(instance, name);
1075 }
1076 }
1077 }
1078
1079 FCITX_EXPORT_API
FcitxInstanceSwitchIMByIndex(FcitxInstance * instance,int index)1080 void FcitxInstanceSwitchIMByIndex(FcitxInstance* instance, int index)
1081 {
1082 UT_array* imes = &instance->imes;
1083 const int iIMCount = utarray_len(imes);
1084 /* less than -2, invalid, set to zero
1085 * -2 scroll back
1086 * -1 scroll forward
1087 * 0~positive select
1088 */
1089 if (index < -4 || index >= iIMCount)
1090 return;
1091
1092 boolean skipZero = (index == -3 || index == -4);
1093 if (index == -2 || index == -4) {
1094 if (instance->iIMIndex > 0) {
1095 index = instance->iIMIndex -1;
1096 if (index == 0 && skipZero)
1097 index = iIMCount - 1;
1098 }
1099 else
1100 index = iIMCount - 1;
1101 } else if (index == -1 || index == -3) {
1102 if (instance->iIMIndex >= (iIMCount - 1))
1103 index = skipZero ? 1 : 0;
1104 else
1105 index = instance->iIMIndex + 1;
1106 }
1107 FcitxInputContext* ic = FcitxInstanceGetCurrentIC(instance);
1108 if (index == 0)
1109 FcitxInstanceCloseIM(instance, ic);
1110 else {
1111 if (ic)
1112 FcitxInstanceSetLocalIMName(instance, ic, NULL);
1113 FcitxInstanceSwitchIMInternal(instance, index, true, true, true);
1114 FcitxInstanceShowInputSpeed(instance, false);
1115
1116 if (FcitxInstanceGetCurrentState(instance) != IS_ACTIVE) {
1117 FcitxInstanceEnableIM(instance, FcitxInstanceGetCurrentIC(instance), false);
1118 }
1119 }
1120 }
1121
1122 FCITX_EXPORT_API
FcitxInstanceUnregisterIM(FcitxInstance * instance,const char * name)1123 void FcitxInstanceUnregisterIM(FcitxInstance* instance, const char* name)
1124 {
1125 FcitxIM* im = FcitxInstanceGetIMFromIMList(instance, IMAS_Disable, name);
1126 if (!im)
1127 return;
1128
1129 unsigned int index = utarray_eltidx(&instance->availimes, im);
1130 utarray_erase(&instance->availimes, index, 1);
1131 }
1132
1133 void
FcitxInstanceSwitchIMInternal(FcitxInstance * instance,int index,boolean skipZero,boolean updateGlobal,boolean userSwitchIM)1134 FcitxInstanceSwitchIMInternal(FcitxInstance* instance, int index,
1135 boolean skipZero, boolean updateGlobal, boolean userSwitchIM)
1136 {
1137 UT_array* imes = &instance->imes;
1138 const int iIMCount = utarray_len(imes);
1139
1140 FcitxIM* lastIM, *newIM;
1141
1142 /* set lastIM */
1143 lastIM = fcitx_array_eltptr(imes, instance->iIMIndex);
1144
1145 if (lastIM) {
1146
1147 if (userSwitchIM) {
1148 if (lastIM->OnClose) {
1149 lastIM->OnClose(lastIM->klass, CET_SwitchIM);
1150 }
1151 }
1152 if (lastIM->Save) {
1153 lastIM->Save(lastIM->klass);
1154 }
1155 }
1156
1157 FcitxInstanceCleanInputWindow(instance);
1158 FcitxInstanceResetInput(instance);
1159 FcitxUIUpdateInputWindow(instance);
1160
1161 /* update instance->iIMIndex start */
1162 if (index >= iIMCount)
1163 instance->iIMIndex = iIMCount - 1;
1164 else if (index < 0)
1165 instance->iIMIndex = 0;
1166 else if (index >= 0)
1167 instance->iIMIndex = index;
1168
1169 if (skipZero && instance->iIMIndex == 0) {
1170 instance->iIMIndex = 1;
1171 }
1172 if (iIMCount <= 1) {
1173 instance->iIMIndex = 0;
1174 }
1175 /* update instance->iIMIndex end */
1176
1177 /* set newIM */
1178 newIM = fcitx_array_eltptr(imes, instance->iIMIndex);
1179
1180 /* lazy load */
1181 if (newIM && !newIM->initialized) {
1182 char* name = strdup(newIM->uniqueName);
1183 FcitxInstanceLoadIM(instance, newIM->owner);
1184 FcitxIM* im = FcitxInstanceGetIMFromIMList(instance, IMAS_Disable, name);
1185 if (!im->initialized) {
1186 im->initialized = true;
1187 unsigned int index = utarray_eltidx(&instance->availimes, im);
1188 utarray_erase(&instance->availimes, index, 1);
1189 }
1190
1191 FcitxInstanceUpdateIMList(instance);
1192 instance->iIMIndex = FcitxInstanceGetIMIndexByName(instance, name);
1193 newIM = fcitx_array_eltptr(imes, instance->iIMIndex);
1194 free(name);
1195 }
1196
1197 if (newIM && newIM->Init) {
1198 FcitxInstanceResetContext(instance, FCF_ResetOnInputMethodChange);
1199 FcitxInstanceSetContext(instance, CONTEXT_IM_LANGUAGE, newIM->langCode);
1200 newIM->Init(newIM->klass);
1201 }
1202
1203 if (newIM && updateGlobal) {
1204 if (fcitx_utils_strcmp0(instance->globalIMName, newIM->uniqueName) != 0) {
1205 fcitx_utils_free(instance->globalIMName);
1206 instance->globalIMName = strdup(newIM->uniqueName);
1207 FcitxProfileSave(instance->profile);
1208 }
1209 }
1210
1211 FcitxInstanceResetInput(instance);
1212 FcitxInstanceProcessIMChangedHook(instance);
1213 }
1214
1215 /**
1216 * 重置输入状态
1217 */
1218 FCITX_EXPORT_API
FcitxInstanceResetInput(FcitxInstance * instance)1219 void FcitxInstanceResetInput(FcitxInstance* instance)
1220 {
1221 FcitxInputState *input = instance->input;
1222 FcitxCandidateWordReset(input->candList);
1223 input->iCursorPos = 0;
1224 input->iClientCursorPos = 0;
1225
1226 FcitxInputStateGetRawInputBuffer(input)[0] = '\0';
1227 input->iCodeInputCount = 0;
1228
1229 input->bIsDoInputOnly = false;
1230 input->bIsInRemind = false;
1231
1232 UT_array* ims = &instance->imes;
1233
1234 FcitxIM *currentIM = fcitx_array_eltptr(ims, instance->iIMIndex);
1235
1236 if (currentIM && currentIM->ResetIM)
1237 currentIM->ResetIM(currentIM->klass);
1238
1239 FcitxInstanceProcessResetInputHook(instance);
1240 }
1241
1242 FCITX_EXPORT_API
FcitxInstanceSendCloseEvent(struct _FcitxInstance * instance,FcitxIMCloseEventType closeEvent)1243 void FcitxInstanceSendCloseEvent(struct _FcitxInstance* instance, FcitxIMCloseEventType closeEvent)
1244 {
1245 FcitxIM* currentIM = FcitxInstanceGetCurrentIM(instance);
1246 if (currentIM && currentIM->OnClose) {
1247 currentIM->OnClose(currentIM->klass, closeEvent);
1248 }
1249 }
1250
FcitxInstanceDoPhraseTips(FcitxInstance * instance)1251 void FcitxInstanceDoPhraseTips(FcitxInstance* instance)
1252 {
1253 UT_array *ims = &instance->imes;
1254 FcitxIM *currentIM = fcitx_array_eltptr(ims, instance->iIMIndex);
1255 FcitxInputState *input = instance->input;
1256
1257 if (currentIM->PhraseTips && currentIM->PhraseTips(currentIM->klass))
1258 input->lastIsSingleHZ = -1;
1259 else
1260 input->lastIsSingleHZ = 0;
1261 }
1262
ImProcessEnter(void * arg)1263 INPUT_RETURN_VALUE ImProcessEnter(void *arg)
1264 {
1265 FcitxInstance *instance = (FcitxInstance *)arg;
1266 INPUT_RETURN_VALUE retVal = IRV_TO_PROCESS;
1267 FcitxInputState *input = instance->input;
1268
1269 if (!input->iCodeInputCount)
1270 retVal = IRV_DONOT_PROCESS;
1271 else {
1272 FcitxInstanceCleanInputWindow(instance);
1273 strcpy(FcitxInputStateGetOutputString(input), FcitxInputStateGetRawInputBuffer(input));
1274 retVal = IRV_ENG;
1275 }
1276 return retVal;
1277 }
1278
ImProcessEscape(void * arg)1279 INPUT_RETURN_VALUE ImProcessEscape(void* arg)
1280 {
1281 FcitxInstance *instance = (FcitxInstance*) arg;
1282 FcitxInputState *input = instance->input;
1283 if (input->iCodeInputCount || input->bIsInRemind)
1284 return IRV_CLEAN;
1285 else
1286 return IRV_DONOT_PROCESS;
1287 }
1288
ImProcessRemind(void * arg)1289 INPUT_RETURN_VALUE ImProcessRemind(void* arg)
1290 {
1291 FcitxInstance *instance = (FcitxInstance*) arg;
1292 FcitxUIUpdateStatus(instance, "remind");
1293 return IRV_DONOT_PROCESS;
1294 }
1295
ImProcessReload(void * arg)1296 INPUT_RETURN_VALUE ImProcessReload(void *arg)
1297 {
1298 FcitxInstance *instance = (FcitxInstance*) arg;
1299 FcitxInstanceReloadConfig(instance);
1300 return IRV_DO_NOTHING;
1301 }
1302
FcitxInstanceReloadAddon(FcitxInstance * instance)1303 void FcitxInstanceReloadAddon(FcitxInstance* instance)
1304 {
1305 FcitxAddon* addonHead = FcitxAddonsLoadInternal(&instance->addons, true);
1306 FcitxInstanceFillAddonOwner(instance, addonHead);
1307 FcitxInstanceResolveAddonDependencyInternal(instance, addonHead);
1308 FcitxInstanceLoadAllIM(instance);
1309 }
1310
1311 FCITX_EXPORT_API
FcitxInstanceReloadAddonConfig(FcitxInstance * instance,const char * addonname)1312 void FcitxInstanceReloadAddonConfig(FcitxInstance *instance, const char* addonname)
1313 {
1314 if (!addonname)
1315 return;
1316
1317 if (strcmp(addonname, "global") == 0) {
1318 if (!FcitxGlobalConfigLoad(instance->config))
1319 FcitxInstanceEnd(instance);
1320
1321 FcitxCandidateWordSetPageSize(instance->input->candList, instance->config->iMaxCandWord);
1322 } else if (strcmp(addonname, "profile") == 0) {
1323 if (!FcitxProfileLoad(instance->profile, instance))
1324 FcitxInstanceEnd(instance);
1325 } else if (strcmp(addonname, "ui") == 0) {
1326 if (instance->ui && instance->ui->ui->ReloadConfig)
1327 instance->ui->ui->ReloadConfig(instance->ui->addonInstance);
1328 } else if (strcmp(addonname, "addon") == 0) {
1329 instance->eventflag |= FEF_RELOAD_ADDON;
1330 } else {
1331 do {
1332 FcitxIM* im;
1333 im = FcitxInstanceGetIMByName(instance, addonname);
1334 if (im && im->ReloadConfig) {
1335 im->ReloadConfig(im->klass);
1336 break;
1337 }
1338
1339 FcitxAddon *addon = FcitxAddonsGetAddonByName(&instance->addons, addonname);
1340 if (!addon || !addon->bEnabled || !addon->addonInstance)
1341 break;
1342 switch (addon->category) {
1343 case AC_MODULE:
1344 if (addon->module->ReloadConfig)
1345 addon->module->ReloadConfig(addon->addonInstance);
1346 break;
1347 case AC_UI:
1348 if (addon->ui->ReloadConfig)
1349 addon->ui->ReloadConfig(addon->addonInstance);
1350 break;
1351 case AC_FRONTEND:
1352 if (addon->frontend->ReloadConfig)
1353 addon->frontend->ReloadConfig(addon->addonInstance);
1354 break;
1355 case AC_INPUTMETHOD:
1356 /* imclass and imclass2 are in same union, only check one of them */
1357 if (addon->imclass) {
1358 for (im = (FcitxIM*) utarray_front(&instance->availimes);
1359 im != NULL;
1360 im = (FcitxIM*) utarray_next(&instance->availimes, im)) {
1361 if (im->owner == addon && im->ReloadConfig) {
1362 im->ReloadConfig(im->klass);
1363 }
1364 }
1365
1366 if (addon->isIMClass2 && addon->imclass2->ReloadConfig) {
1367 addon->imclass2->ReloadConfig(addon->addonInstance);
1368 }
1369 }
1370 break;
1371 default:
1372 break;
1373 }
1374 } while(0);
1375 }
1376 }
1377
1378 FCITX_EXPORT_API
FcitxInstanceReloadConfig(FcitxInstance * instance)1379 void FcitxInstanceReloadConfig(FcitxInstance *instance)
1380 {
1381 if (!FcitxGlobalConfigLoad(instance->config))
1382 FcitxInstanceEnd(instance);
1383
1384 if (!FcitxProfileLoad(instance->profile, instance))
1385 FcitxInstanceEnd(instance);
1386
1387 FcitxCandidateWordSetPageSize(instance->input->candList, instance->config->iMaxCandWord);
1388
1389 /* Reload All IM, Module, and UI Config */
1390 UT_array* addons = &instance->addons;
1391
1392 FcitxAddon *addon;
1393 for (addon = (FcitxAddon *) utarray_front(addons);
1394 addon != NULL;
1395 addon = (FcitxAddon *) utarray_next(addons, addon)) {
1396 if (addon->category == AC_MODULE &&
1397 addon->bEnabled &&
1398 addon->addonInstance) {
1399 if (addon->module->ReloadConfig)
1400 addon->module->ReloadConfig(addon->addonInstance);
1401 }
1402 }
1403
1404 for (addon = (FcitxAddon *) utarray_front(addons);
1405 addon != NULL;
1406 addon = (FcitxAddon *) utarray_next(addons, addon)) {
1407 if (addon->category == AC_FRONTEND &&
1408 addon->bEnabled &&
1409 addon->addonInstance) {
1410 if (addon->frontend->ReloadConfig)
1411 addon->frontend->ReloadConfig(addon->addonInstance);
1412 }
1413 }
1414
1415 for (addon = (FcitxAddon *) utarray_front(addons);
1416 addon != NULL;
1417 addon = (FcitxAddon *) utarray_next(addons, addon)) {
1418 if (addon->category == AC_INPUTMETHOD &&
1419 addon->bEnabled &&
1420 addon->addonInstance &&
1421 addon->isIMClass2) {
1422 if (addon->imclass2->ReloadConfig)
1423 addon->imclass2->ReloadConfig(addon->addonInstance);
1424 }
1425 }
1426
1427
1428 UT_array* imes = &instance->imes;
1429 FcitxIM* pim;
1430 for (pim = (FcitxIM *) utarray_front(imes);
1431 pim != NULL;
1432 pim = (FcitxIM *) utarray_next(imes, pim)) {
1433 if (pim->ReloadConfig)
1434 pim->ReloadConfig(pim->klass);
1435 }
1436
1437 if (instance->ui && instance->ui->ui->ReloadConfig)
1438 instance->ui->ui->ReloadConfig(instance->ui->addonInstance);
1439
1440 instance->eventflag |= FEF_RELOAD_ADDON;
1441 }
1442
FcitxInstanceSetICStatus(FcitxInstance * instance,FcitxInputContext * ic,FcitxContextState state)1443 static inline void FcitxInstanceSetICStatus(FcitxInstance* instance, FcitxInputContext* ic, FcitxContextState state)
1444 {
1445 if (ic->state != state) {
1446 ic->state = state;
1447 FcitxInstanceProcessICStateChangedHook(instance, ic);
1448 }
1449 }
1450
1451 FCITX_EXPORT_API
FcitxInstanceSetLocalIMName(FcitxInstance * instance,FcitxInputContext * ic,const char * imname)1452 void FcitxInstanceSetLocalIMName(FcitxInstance* instance, FcitxInputContext* ic, const char* imname)
1453 {
1454 FcitxInputContext2* ic2 = (FcitxInputContext2*) ic;
1455 if (ic2->imname) {
1456 free(ic2->imname);
1457 ic2->imname = NULL;
1458 }
1459
1460 if (imname)
1461 ic2->imname = strdup(imname);
1462
1463 if (ic == FcitxInstanceGetCurrentIC(instance)) {
1464 FcitxInstanceUpdateCurrentIM(instance, false, true);
1465 }
1466 }
1467
1468 /* the "force" to this function is used when the list changed and index is not valid */
FcitxInstanceUpdateCurrentIM(FcitxInstance * instance,boolean force,boolean userSwitchIM)1469 boolean FcitxInstanceUpdateCurrentIM(FcitxInstance* instance, boolean force, boolean userSwitchIM) {
1470 FcitxInputContext* ic = FcitxInstanceGetCurrentIC(instance);
1471 if (!ic && !force)
1472 return false;
1473 FcitxInputContext2* ic2 = (FcitxInputContext2*) ic;
1474 int globalIndex = FcitxInstanceGetIMIndexByName(instance, instance->globalIMName);
1475 boolean forceSwtich = force;
1476 boolean updateGlobal = false;
1477 /* global index is not valid, that's why we need to fix it. */
1478 if (globalIndex <= 0) {
1479 UT_array *ime = &instance->imes;
1480 FcitxIM *im = (FcitxIM*)utarray_eltptr(ime, 1);
1481 if (im) {
1482 fcitx_utils_string_swap(&instance->globalIMName, im->uniqueName);
1483 globalIndex = 1;
1484 forceSwtich = true;
1485 updateGlobal = true;
1486 }
1487 }
1488 int targetIMIndex = 0;
1489 boolean skipZero = false;
1490
1491 if (ic2 && ic2->imname) {
1492 FcitxIM* im = FcitxInstanceGetIMFromIMList(instance, IMAS_Enable, ic2->imname);
1493 if (!im) {
1494 free(ic2->imname);
1495 ic2->imname = NULL;
1496 }
1497 }
1498
1499 if (ic && ic->state != IS_ACTIVE) {
1500 targetIMIndex = 0;
1501 }
1502 else {
1503 if (ic2 && ic2->imname)
1504 targetIMIndex = FcitxInstanceGetIMIndexByName(instance, ic2->imname);
1505 else
1506 targetIMIndex = globalIndex;
1507 skipZero = true;
1508 }
1509
1510 if (forceSwtich || targetIMIndex != instance->iIMIndex) {
1511 FcitxInstanceSwitchIMInternal(instance, targetIMIndex, skipZero, updateGlobal, userSwitchIM);
1512 return true;
1513 }
1514 else
1515 return false;
1516 }
1517
1518 FCITX_EXPORT_API
FcitxInstanceEnableIM(FcitxInstance * instance,FcitxInputContext * ic,boolean keepState)1519 void FcitxInstanceEnableIM(FcitxInstance* instance, FcitxInputContext* ic, boolean keepState)
1520 {
1521 if (ic == NULL)
1522 return;
1523 instance->globalState = IS_ACTIVE;
1524 switch (instance->config->shareState) {
1525 case ShareState_All:
1526 case ShareState_PerProgram: {
1527 FcitxInputContext *rec = instance->ic_list;
1528 while (rec != NULL) {
1529 boolean flag = false;
1530 if (instance->config->shareState == ShareState_All)
1531 flag = true;
1532 else {
1533 flag = FcitxInstanceCheckICFromSameApplication(instance, rec, ic);
1534 }
1535
1536 if (flag && (rec == ic || !(rec->contextCaps & CAPACITY_CLIENT_SIDE_CONTROL_STATE)))
1537 FcitxInstanceEnableIMInternal(instance, rec, keepState);
1538 rec = rec->next;
1539 }
1540 }
1541 break;
1542 case ShareState_None:
1543 FcitxInstanceEnableIMInternal(instance, ic, keepState);
1544 break;
1545 }
1546
1547 FcitxInstanceUpdateCurrentIM(instance, false, false);
1548 instance->input->keyReleased = KR_OTHER;
1549 }
1550
1551
FcitxInstanceEnableIMInternal(FcitxInstance * instance,FcitxInputContext * ic,boolean keepState)1552 void FcitxInstanceEnableIMInternal(FcitxInstance* instance, FcitxInputContext* ic, boolean keepState)
1553 {
1554 if (ic == NULL)
1555 return ;
1556 FcitxAddon **pfrontend = FcitxInstanceGetPFrontend(instance, ic->frontendid);
1557 if (pfrontend == NULL)
1558 return;
1559 FcitxFrontend* frontend = (*pfrontend)->frontend;
1560 FcitxContextState oldstate = ic->state;
1561 FcitxInstanceSetICStatus(instance, ic, IS_ACTIVE);
1562 if (oldstate == IS_CLOSED)
1563 frontend->EnableIM((*pfrontend)->addonInstance, ic);
1564
1565 if (ic == instance->CurrentIC) {
1566 if (oldstate == IS_CLOSED)
1567 FcitxUIOnTriggerOn(instance);
1568 if (!keepState)
1569 FcitxInstanceResetInput(instance);
1570 }
1571 }
1572
1573 FCITX_EXPORT_API
FcitxInstanceCloseIM(FcitxInstance * instance,FcitxInputContext * ic)1574 void FcitxInstanceCloseIM(FcitxInstance* instance, FcitxInputContext* ic)
1575 {
1576 if (ic == NULL)
1577 return;
1578
1579 if (!(ic->contextCaps & CAPACITY_CLIENT_SIDE_CONTROL_STATE)) {
1580 if (ic->state == IS_ACTIVE)
1581 FcitxInstanceChangeIMState(instance, ic);
1582 return;
1583 }
1584
1585 instance->globalState = IS_CLOSED;
1586 switch (instance->config->shareState) {
1587 case ShareState_All:
1588 case ShareState_PerProgram: {
1589 FcitxInputContext *rec = instance->ic_list;
1590 while (rec != NULL) {
1591 boolean flag = false;
1592 if (instance->config->shareState == ShareState_All)
1593 flag = true;
1594 else {
1595 flag = FcitxInstanceCheckICFromSameApplication(instance, rec, ic);
1596 }
1597
1598 if (flag && (rec == ic || !(rec->contextCaps & CAPACITY_CLIENT_SIDE_CONTROL_STATE)))
1599 FcitxInstanceCloseIMInternal(instance, rec);
1600 rec = rec->next;
1601 }
1602 }
1603 break;
1604 case ShareState_None:
1605 FcitxInstanceCloseIMInternal(instance, ic);
1606 break;
1607 }
1608 }
1609
FcitxInstanceCloseIMInternal(FcitxInstance * instance,FcitxInputContext * ic)1610 void FcitxInstanceCloseIMInternal(FcitxInstance* instance, FcitxInputContext* ic)
1611 {
1612 if (ic == NULL)
1613 return ;
1614 FcitxAddon **pfrontend = FcitxInstanceGetPFrontend(instance, ic->frontendid);
1615 if (pfrontend == NULL)
1616 return;
1617 FcitxFrontend* frontend = (*pfrontend)->frontend;
1618 FcitxInstanceSetICStatus(instance, ic, IS_CLOSED);
1619 frontend->CloseIM((*pfrontend)->addonInstance, ic);
1620
1621 if (ic == instance->CurrentIC) {
1622 FcitxUIOnTriggerOff(instance);
1623 FcitxUICloseInputWindow(instance);
1624 FcitxInstanceResetInput(instance);
1625 }
1626 }
1627
FcitxInstanceChangeIMStateWithKey(FcitxInstance * instance,FcitxInputContext * ic,boolean withSwitchKey)1628 void FcitxInstanceChangeIMStateWithKey(FcitxInstance* instance, FcitxInputContext* ic, boolean withSwitchKey)
1629 {
1630 if (ic == NULL)
1631 return;
1632 FcitxContextState objectState;
1633 if (ic->state == IS_INACTIVE)
1634 objectState = IS_ACTIVE;
1635 else
1636 objectState = IS_INACTIVE;
1637
1638 instance->globalState = objectState;
1639 switch (instance->config->shareState) {
1640 case ShareState_All:
1641 case ShareState_PerProgram: {
1642 FcitxInputContext *rec = instance->ic_list;
1643 while (rec != NULL) {
1644 boolean flag = false;
1645 if (instance->config->shareState == ShareState_All)
1646 flag = true;
1647 else {
1648 flag = FcitxInstanceCheckICFromSameApplication(instance, rec, ic);
1649 }
1650
1651 if (flag && (rec == ic || !(rec->contextCaps & CAPACITY_CLIENT_SIDE_CONTROL_STATE)))
1652 FcitxInstanceChangeIMStateInternal(instance, rec, objectState, withSwitchKey);
1653 rec = rec->next;
1654 }
1655 }
1656 break;
1657 case ShareState_None:
1658 FcitxInstanceChangeIMStateInternal(instance, ic, objectState, withSwitchKey);
1659 break;
1660 }
1661
1662 FcitxInstanceUpdateCurrentIM(instance, false, !withSwitchKey);
1663 }
1664
1665 /**
1666 * 更改输入法状态
1667 *
1668 * @param _connect_id
1669 */
1670 FCITX_EXPORT_API
FcitxInstanceChangeIMState(FcitxInstance * instance,FcitxInputContext * ic)1671 void FcitxInstanceChangeIMState(FcitxInstance* instance, FcitxInputContext* ic)
1672 {
1673 FcitxInstanceChangeIMStateWithKey(instance, ic, false);
1674 }
1675
FcitxInstanceChangeIMStateInternal(FcitxInstance * instance,FcitxInputContext * ic,FcitxContextState objectState,boolean withSwitchKey)1676 void FcitxInstanceChangeIMStateInternal(FcitxInstance* instance, FcitxInputContext* ic, FcitxContextState objectState, boolean withSwitchKey)
1677 {
1678 if (!ic)
1679 return;
1680 if (ic->state == objectState)
1681 return;
1682 FcitxInstanceSetICStatus(instance, ic, objectState);
1683 FcitxInputContext2* ic2 = (FcitxInputContext2*) ic;
1684 ic2->switchBySwitchKey = withSwitchKey;
1685 if (ic == instance->CurrentIC) {
1686 if (objectState != IS_ACTIVE) {
1687 FcitxUICloseInputWindow(instance);
1688 }
1689 }
1690 }
1691
FcitxInstanceInitIMMenu(FcitxInstance * instance)1692 void FcitxInstanceInitIMMenu(FcitxInstance* instance)
1693 {
1694 FcitxMenuInit(&instance->imMenu);
1695 instance->imMenu.candStatusBind = NULL;
1696 instance->imMenu.name = strdup(_("Input Method"));
1697
1698 instance->imMenu.UpdateMenu = UpdateIMMenuItem;
1699 instance->imMenu.MenuAction = IMMenuAction;
1700 instance->imMenu.priv = instance;
1701 instance->imMenu.isSubMenu = false;
1702 }
1703
IMMenuAction(FcitxUIMenu * menu,int index)1704 boolean IMMenuAction(FcitxUIMenu *menu, int index)
1705 {
1706 FcitxInstance* instance = (FcitxInstance*) menu->priv;
1707
1708 FcitxIM* im = FcitxInstanceGetIMByIndex(instance, index);
1709 // this contains delay support, so we don't use switch im by index here.
1710 if (im) {
1711 FcitxInstanceSwitchIMByName(instance, im->uniqueName);
1712 }
1713 return true;
1714 }
1715
UpdateIMMenuItem(FcitxUIMenu * menu)1716 void UpdateIMMenuItem(FcitxUIMenu *menu)
1717 {
1718 FcitxInstance* instance = (FcitxInstance*) menu->priv;
1719 FcitxMenuClear(menu);
1720
1721 FcitxIM* pim;
1722 UT_array* imes = &instance->imes;
1723 for (pim = (FcitxIM *) utarray_front(imes);
1724 pim != NULL;
1725 pim = (FcitxIM *) utarray_next(imes, pim))
1726 FcitxMenuAddMenuItem(&instance->imMenu, pim->strName, MENUTYPE_SIMPLE, NULL);
1727
1728 menu->mark = instance->iIMIndex;
1729 }
1730
HideInputSpeed(void * arg)1731 void HideInputSpeed(void* arg)
1732 {
1733 FcitxInstance *instance = arg;
1734 FcitxInputState* input = instance->input;
1735 if (FcitxMessagesIsMessageChanged(input->msgAuxUp)
1736 || FcitxMessagesIsMessageChanged(input->msgAuxDown)
1737 || FcitxMessagesGetMessageCount(input->msgPreedit)
1738 || FcitxMessagesGetMessageCount(input->msgClientPreedit)
1739 || FcitxCandidateWordGetListSize(input->candList))
1740 return;
1741 FcitxUICloseInputWindow(instance);
1742 }
1743
FcitxInstanceShowInputSpeed(FcitxInstance * instance,boolean force)1744 void FcitxInstanceShowInputSpeed(FcitxInstance* instance, boolean force)
1745 {
1746 FcitxInputState* input = instance->input;
1747
1748 if (!instance->initialized)
1749 return;
1750
1751 if (!force) {
1752 if (!instance->config->bShowInputWindowTriggering)
1753 return;
1754
1755 if (FcitxInstanceGetCurrentState(instance) != IS_ACTIVE && instance->config->bShowInputWindowOnlyWhenActive)
1756 return;
1757 }
1758
1759 if (FcitxMessagesGetMessageCount(input->msgAuxUp)
1760 || FcitxMessagesGetMessageCount(input->msgAuxDown)
1761 || FcitxMessagesGetMessageCount(input->msgPreedit)
1762 || FcitxMessagesGetMessageCount(input->msgClientPreedit)
1763 || FcitxCandidateWordGetListSize(input->candList))
1764 return;
1765
1766 input->bShowCursor = false;
1767
1768 FcitxInstanceCleanInputWindow(instance);
1769
1770 FcitxIM* im = FcitxInstanceGetCurrentIM(instance);
1771 if (!im)
1772 return;
1773
1774 if (instance->config->bShowVersion) {
1775 FcitxMessagesAddMessageStringsAtLast(input->msgAuxUp, MSG_TIPS,
1776 "FCITX " VERSION " ");
1777 }
1778 if (im) {
1779 FcitxMessagesAddMessageStringsAtLast(input->msgAuxUp, MSG_TIPS,
1780 im->strName);
1781 const char* subModeName = im->GetSubModeName ? im->GetSubModeName(im->klass) : NULL;
1782 if (subModeName) {
1783 FcitxMessagesAddMessageStringsAtLast(input->msgAuxUp, MSG_TIPS, " - ",
1784 subModeName);
1785 }
1786 }
1787
1788 //显示打字速度
1789 if (instance->config->bShowUserSpeed) {
1790 double timePassed;
1791
1792 timePassed = instance->totaltime + difftime(time(NULL), instance->timeStart);
1793 if (((int) timePassed) == 0)
1794 timePassed = 1.0;
1795
1796 FcitxMessagesSetMessageCount(input->msgAuxDown, 0);
1797 FcitxMessagesAddMessageStringsAtLast(input->msgAuxDown, MSG_OTHER,
1798 _("Input Speed: "));
1799 FcitxMessagesAddMessageAtLast(input->msgAuxDown, MSG_CODE, "%d", (int)(instance->iHZInputed * 60 / timePassed));
1800 FcitxMessagesAddMessageStringsAtLast(input->msgAuxDown, MSG_OTHER,
1801 _("/min Time Used: "));
1802 FcitxMessagesAddMessageAtLast(input->msgAuxDown, MSG_CODE, "%d", (int) timePassed / 60);
1803 FcitxMessagesAddMessageStringsAtLast(input->msgAuxDown, MSG_OTHER,
1804 _("min Num of Characters: "));
1805 FcitxMessagesAddMessageAtLast(input->msgAuxDown, MSG_CODE, "%u", instance->iHZInputed);
1806 }
1807
1808 FcitxUIUpdateInputWindow(instance);
1809
1810 if (!FcitxInstanceCheckTimeoutByFunc(instance, HideInputSpeed))
1811 FcitxInstanceAddTimeout(instance, 1000, HideInputSpeed, instance);
1812 }
1813
1814
ImProcessSaveAll(void * arg)1815 INPUT_RETURN_VALUE ImProcessSaveAll(void *arg)
1816 {
1817 FcitxInstance *instance = (FcitxInstance*) arg;
1818 FcitxInstanceSaveAllIM(instance);
1819 return IRV_DO_NOTHING;
1820 }
1821
1822
ImSwitchEmbeddedPreedit(void * arg)1823 INPUT_RETURN_VALUE ImSwitchEmbeddedPreedit(void *arg)
1824 {
1825 FcitxInstance *instance = (FcitxInstance*) arg;
1826 instance->profile->bUsePreedit = !instance->profile->bUsePreedit;
1827 FcitxProfileSave(instance->profile);
1828 FcitxUIUpdateInputWindow(instance);
1829 return IRV_DO_NOTHING;
1830 }
1831
1832 FCITX_EXPORT_API
FcitxInstanceCleanInputWindow(FcitxInstance * instance)1833 void FcitxInstanceCleanInputWindow(FcitxInstance *instance)
1834 {
1835 FcitxInstanceCleanInputWindowUp(instance);
1836 FcitxInstanceCleanInputWindowDown(instance);
1837 }
1838
1839 FCITX_EXPORT_API
FcitxInstanceCleanInputWindowUp(FcitxInstance * instance)1840 void FcitxInstanceCleanInputWindowUp(FcitxInstance *instance)
1841 {
1842 FcitxInputState* input = instance->input;
1843 FcitxMessagesSetMessageCount(input->msgAuxUp, 0);
1844 FcitxMessagesSetMessageCount(input->msgPreedit, 0);
1845 FcitxMessagesSetMessageCount(input->msgClientPreedit, 0);
1846 }
1847
1848 FCITX_EXPORT_API
FcitxInstanceCleanInputWindowDown(FcitxInstance * instance)1849 void FcitxInstanceCleanInputWindowDown(FcitxInstance* instance)
1850 {
1851 FcitxInputState* input = instance->input;
1852 FcitxCandidateWordReset(input->candList);
1853 FcitxMessagesSetMessageCount(input->msgAuxDown, 0);
1854 }
1855
1856 FCITX_EXPORT_API
FcitxHotkeyCheckChooseKey(FcitxKeySym sym,unsigned int state,const char * strChoose)1857 int FcitxHotkeyCheckChooseKey(FcitxKeySym sym, unsigned int state, const char* strChoose)
1858 {
1859 return FcitxHotkeyCheckChooseKeyAndModifier(sym, state, strChoose, FcitxKeyState_None);
1860 }
1861
1862 FCITX_EXPORT_API
FcitxHotkeyCheckChooseKeyAndModifier(FcitxKeySym sym,unsigned int state,const char * strChoose,int candState)1863 int FcitxHotkeyCheckChooseKeyAndModifier(FcitxKeySym sym, unsigned int state,
1864 const char* strChoose, int candState)
1865 {
1866 if (state != (unsigned)candState)
1867 return -1;
1868
1869 char *p = strchr(strChoose, FcitxHotkeyPadToMain(sym));
1870 return p ? p - strChoose : -1;
1871 }
1872
1873 FCITX_EXPORT_API
FcitxInstanceGetCurrentIM(FcitxInstance * instance)1874 FcitxIM* FcitxInstanceGetCurrentIM(FcitxInstance* instance)
1875 {
1876 return fcitx_array_eltptr(&instance->imes, instance->iIMIndex);
1877 }
1878
1879 FCITX_EXPORT_API
FcitxInstanceGetIMByIndex(FcitxInstance * instance,int index)1880 FcitxIM* FcitxInstanceGetIMByIndex(FcitxInstance* instance, int index)
1881 {
1882 /* utarray helps us to check the overflow */
1883 return fcitx_array_eltptr(&instance->imes, index);
1884 }
1885
1886 FCITX_EXPORT_API
FcitxInstanceGetIMIndexByName(FcitxInstance * instance,const char * imName)1887 int FcitxInstanceGetIMIndexByName(FcitxInstance* instance, const char* imName)
1888 {
1889 UT_array* imes = &instance->imes;
1890 FcitxIM *pim = FcitxInstanceGetIMByName(instance, imName);
1891 if (!pim)
1892 return -1;
1893 else
1894 return utarray_eltidx(imes, pim);
1895 }
1896
1897 FCITX_EXPORT_API
FcitxInstanceGetIMByName(FcitxInstance * instance,const char * imName)1898 FcitxIM* FcitxInstanceGetIMByName(FcitxInstance* instance, const char* imName)
1899 {
1900 UT_array* imes = &instance->imes;
1901 FcitxIM *pim;
1902 for (pim = (FcitxIM *) utarray_front(imes);
1903 pim != NULL;
1904 pim = (FcitxIM *) utarray_next(imes, pim)) {
1905 if (strcmp(imName, pim->uniqueName) == 0) {
1906 return pim;
1907 }
1908 }
1909 return NULL;
1910 }
1911
1912
1913 FCITX_EXPORT_API
FcitxInstanceNotifyUpdateSurroundingText(FcitxInstance * instance,FcitxInputContext * ic)1914 void FcitxInstanceNotifyUpdateSurroundingText(FcitxInstance* instance, FcitxInputContext* ic)
1915 {
1916 if (!ic)
1917 return;
1918 if (ic != instance->CurrentIC)
1919 return;
1920
1921 FcitxIM* im = FcitxInstanceGetCurrentIM(instance);
1922 if (!im)
1923 return;
1924
1925 if (im->UpdateSurroundingText)
1926 im->UpdateSurroundingText(im->klass);
1927 }
1928
UnusedIMItemFreeAll(UnusedIMItem * item)1929 void UnusedIMItemFreeAll(UnusedIMItem* item)
1930 {
1931 UnusedIMItem *cur;
1932 while (item) {
1933 cur = item;
1934 HASH_DEL(item, cur);
1935 fcitx_utils_free(cur->name);
1936 free(cur);
1937 }
1938 }
1939
MatchLanguage(const char * lang,const char * cur)1940 static inline boolean MatchLanguage(const char* lang, const char* cur) {
1941 /* here we hard code some for chinese */
1942 if (strcmp(cur, "zh_HK") == 0) {
1943 cur = "zh_TW";
1944 }
1945 if (strcmp(cur, "en_HK") == 0) {
1946 cur = "zh_TW";
1947 }
1948 if (strcmp(lang, "zh_HK") == 0) {
1949 lang = "zh_TW";
1950 }
1951
1952 if (strncmp(cur, "zh", 2) == 0 && strlen(cur) == 5) {
1953 return strcmp(lang, cur) == 0;
1954 }
1955
1956 return strncmp(lang, cur, 2) == 0;
1957 }
1958
1959 FCITX_EXPORT_API
FcitxInstanceUpdateIMList(FcitxInstance * instance)1960 void FcitxInstanceUpdateIMList(FcitxInstance* instance)
1961 {
1962 if (!instance->imLoaded)
1963 return;
1964
1965 UT_array* imList = fcitx_utils_split_string(instance->profile->imList, ',');
1966 utarray_sort(&instance->availimes, IMPriorityCmp);
1967 utarray_clear(&instance->imes);
1968 UnusedIMItemFreeAll(instance->unusedItem);
1969 instance->unusedItem = NULL;
1970
1971 boolean imListIsEmpty = utarray_len(imList) == 0;
1972
1973
1974 char** pstr;
1975 FcitxIM* ime;
1976 for (pstr = (char**) utarray_front(imList);
1977 pstr != NULL;
1978 pstr = (char**) utarray_next(imList, pstr)) {
1979 char* str = *pstr;
1980 char* pos = strchr(str, ':');
1981 if (pos) {
1982 ime = NULL;
1983 *pos = '\0';
1984 pos ++;
1985 ime = FcitxInstanceGetIMFromIMList(instance, IMAS_Disable, str);
1986 boolean status = (strcmp(pos, "True") == 0);
1987 if (status && ime)
1988 utarray_push_back(&instance->imes, ime);
1989
1990 if (!ime) {
1991 UnusedIMItem* item;
1992 HASH_FIND_STR(instance->unusedItem, str, item);
1993 if (!item) {
1994 item = fcitx_utils_new(UnusedIMItem);
1995 item->name = strdup(str);
1996 item->status = status;
1997 HASH_ADD_KEYPTR(hh, instance->unusedItem, item->name, strlen(item->name), item);
1998 }
1999 }
2000 }
2001 }
2002
2003 char* lang = fcitx_utils_get_current_langcode();
2004 for (ime = (FcitxIM*) utarray_front(&instance->availimes);
2005 ime != NULL;
2006 ime = (FcitxIM*) utarray_next(&instance->availimes, ime)) {
2007 if (!IMIsInIMNameList(imList, ime)) {
2008 /* ok, make all im priority larger than 100 disable by default */
2009 if (ime->iPriority == 0)
2010 utarray_push_front(&instance->imes, ime);
2011 else if (ime->iPriority < PRIORITY_DISABLE) {
2012 /*
2013 * here, we use such logic, if user start fcitx for first time (or doing reset)
2014 * then match everything by language, otherwise user might just install something,
2015 * then add something else.
2016 */
2017 if (!imListIsEmpty || MatchLanguage(ime->langCode, lang)) {
2018 utarray_push_back(&instance->imes, ime);
2019 }
2020 }
2021 }
2022 }
2023 free(lang);
2024
2025 utarray_free(imList);
2026
2027 FcitxInstanceUpdateCurrentIM(instance, true, false);
2028 FcitxInstanceProcessUpdateIMListHook(instance);
2029
2030 if (instance->globalIMName)
2031 FcitxProfileSave(instance->profile);
2032 }
2033
2034 FCITX_EXPORT_API
FcitxInstanceGetIMFromIMList(FcitxInstance * instance,FcitxIMAvailableStatus status,const char * name)2035 FcitxIM* FcitxInstanceGetIMFromIMList(FcitxInstance* instance, FcitxIMAvailableStatus status, const char* name)
2036 {
2037 UT_array* imes;
2038 if (status == IMAS_Enable)
2039 imes = &instance->imes;
2040 else
2041 imes = &instance->availimes;
2042 FcitxIM* ime = NULL;
2043 for (ime = (FcitxIM*) utarray_front(imes);
2044 ime != NULL;
2045 ime = (FcitxIM*) utarray_next(imes, ime)) {
2046 if (strcmp(ime->uniqueName, name) == 0)
2047 break;
2048 }
2049 return ime;
2050 }
2051
IMIsInIMNameList(UT_array * imList,FcitxIM * ime)2052 boolean IMIsInIMNameList(UT_array* imList, FcitxIM* ime)
2053 {
2054 char** pstr;
2055 for (pstr = (char**) utarray_front(imList);
2056 pstr != NULL;
2057 pstr = (char**) utarray_next(imList, pstr)) {
2058 if (strncmp(ime->uniqueName, *pstr, strlen(ime->uniqueName)) == 0)
2059 return true;
2060 }
2061 return false;
2062 }
2063
FreeIMEntry(FcitxIMEntry * entry)2064 void FreeIMEntry(FcitxIMEntry* entry)
2065 {
2066 if (!entry)
2067 return ;
2068 FcitxConfigFreeConfigFile(entry->config.configFile);
2069 free(entry->name);
2070 free(entry->iconName);
2071 free(entry->langCode);
2072 free(entry->uniqueName);
2073 free(entry->parent);
2074 free(entry);
2075 }
2076
2077 FCITX_EXPORT_API
FcitxStandardKeyBlocker(FcitxInputState * input,FcitxKeySym key,unsigned int state)2078 INPUT_RETURN_VALUE FcitxStandardKeyBlocker(FcitxInputState* input, FcitxKeySym key, unsigned int state)
2079 {
2080 if ((FcitxInputStateGetRawInputBufferSize(input) != 0
2081 || FcitxMessagesGetMessageCount(input->msgPreedit)
2082 || FcitxMessagesGetMessageCount(input->msgClientPreedit)
2083 || FcitxCandidateWordGetListSize(input->candList))
2084 && (FcitxHotkeyIsHotKeySimple(key, state)
2085 || FcitxHotkeyIsHotkeyCursorMove(key, state)
2086 || FcitxHotkeyIsHotKey(key, state, FCITX_SHIFT_SPACE)
2087 || FcitxHotkeyIsHotKey(key, state, FCITX_TAB)
2088 || FcitxHotkeyIsHotKey(key, state, FCITX_SHIFT_ENTER)
2089 ))
2090 return IRV_DO_NOTHING;
2091 else
2092 return IRV_TO_PROCESS;
2093 }
2094
2095 FCITX_EXPORT_API
FcitxInstanceShowCurrentIMInfo(FcitxInstance * instance)2096 void FcitxInstanceShowCurrentIMInfo(FcitxInstance* instance)
2097 {
2098 FcitxInstanceShowInputSpeed(instance, true);
2099 }
2100
2101 // kate: indent-mode cstyle; space-indent on; indent-width 0;
2102