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