1 /***************************************************************************
2  *   Copyright (C) 2010~2010 by CSSlayer                                   *
3  *   wengxt@gmail.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 #include "config.h"
22 
23 #include <unistd.h>
24 #include <time.h>
25 #include <limits.h>
26 #include <libintl.h>
27 #include <pthread.h>
28 #include <semaphore.h>
29 #include <getopt.h>
30 #include <sys/time.h>
31 #include <signal.h>
32 #include <fcntl.h>
33 #include <regex.h>
34 
35 #include "instance.h"
36 #include "fcitx-utils/log.h"
37 #include "ime-internal.h"
38 #include "ui.h"
39 #include "addon.h"
40 #include "module.h"
41 #include "frontend.h"
42 #include "fcitx-utils/utils.h"
43 #include "candidate.h"
44 #include "ui-internal.h"
45 #include "fcitx-internal.h"
46 #include "instance-internal.h"
47 #include "module-internal.h"
48 #include "addon-internal.h"
49 #include "setjmp.h"
50 
51 #define CHECK_ENV(env, value, icase) (!getenv(env) \
52                                       || (icase ? \
53                                               (0 != strcmp(getenv(env), (value))) \
54                                               : (0 != strcasecmp(getenv(env), (value)))))
55 
56 FCITX_GETTER_REF(FcitxInstance, Addons, addons, UT_array)
57 FCITX_GETTER_REF(FcitxInstance, UIMenus, uimenus, UT_array)
58 FCITX_GETTER_REF(FcitxInstance, UIStats, uistats, UT_array)
59 FCITX_GETTER_REF(FcitxInstance, UIComplexStats, uicompstats, UT_array)
60 FCITX_GETTER_REF(FcitxInstance, IMEs, imes, UT_array)
61 FCITX_GETTER_REF(FcitxInstance, AvailIMEs, availimes, UT_array)
62 FCITX_GETTER_REF(FcitxInstance, ReadFDSet, rfds, fd_set)
63 FCITX_GETTER_REF(FcitxInstance, WriteFDSet, wfds, fd_set)
64 FCITX_GETTER_REF(FcitxInstance, ExceptFDSet, efds, fd_set)
65 FCITX_GETTER_VALUE(FcitxInstance, CurrentUI, ui, FcitxAddon*)
66 FCITX_GETTER_VALUE(FcitxInstance, MaxFD, maxfd, int)
67 FCITX_SETTER(FcitxInstance, MaxFD, maxfd, int)
68 FCITX_GETTER_VALUE(FcitxInstance, GlobalConfig, config, FcitxGlobalConfig*)
69 FCITX_GETTER_VALUE(FcitxInstance, Profile, profile, FcitxProfile*)
70 FCITX_GETTER_VALUE(FcitxInstance, InputState, input, FcitxInputState*)
71 FCITX_GETTER_VALUE(FcitxInstance, IsDestroying, destroy, boolean)
72 
73 static const UT_icd stat_icd = {
74     sizeof(FcitxUIStatus), NULL, NULL, NULL
75 };
76 static const UT_icd compstat_icd = {
77     sizeof(FcitxUIComplexStatus), NULL, NULL, NULL
78 };
79 static const UT_icd timeout_icd = {
80     sizeof(TimeoutItem), NULL, NULL, NULL
81 };
82 static const UT_icd icdata_icd = {
83     sizeof(FcitxICDataInfo), NULL, NULL, NULL
84 };
85 static void FcitxInitThread(FcitxInstance* inst);
86 static void ToggleRemindState(void* arg);
87 static boolean GetRemindEnabled(void* arg);
88 static boolean ProcessOption(FcitxInstance* instance, int argc, char* argv[]);
89 static void Usage();
90 static void Version();
91 static void* RunInstance(void* arg);
92 static void FcitxInstanceInitBuiltContext(FcitxInstance* instance);
93 static void FcitxInstanceShowRemindStatusChanged(void* arg, const void* value);
94 static void FcitxInstanceRealEnd(FcitxInstance* instance);
95 static void FcitxInstanceInitNoPreeditApps(FcitxInstance* instance);
96 
97 /**
98  * 显示命令行参数
99  */
Usage()100 void Usage()
101 {
102     printf("Usage: fcitx [OPTION]\n"
103            "\t-r, --replace\t\ttry replace existing fcitx, need module support.\n"
104            "\t-d\t\t\trun as daemon(default)\n"
105            "\t-D\t\t\tdon't run as daemon\n"
106            "\t-s[sleep time]\t\toverride delay start time in config file, 0 for immediate start\n"
107            "\t-v, --version\t\tdisplay the version information and exit\n"
108            "\t-u, --ui\t\tspecify the user interface to use\n"
109            "\t--enable\t\tspecify a comma separated list for addon that will override the enable option\n"
110            "\t--disable\t\tspecify a comma separated list for addon that will explicitly disabled,\n"
111            "\t\t\t\t\tpriority is lower than --enable, can use all for disable all module\n"
112            "\t-h, --help\t\tdisplay this help and exit\n");
113 }
114 
115 /**
116  * 显示版本
117  */
Version()118 void Version()
119 {
120     printf("fcitx version: %s\n", VERSION);
121 }
122 
123 FCITX_EXPORT_API
FcitxInstanceCreate(sem_t * sem,int argc,char * argv[])124 FcitxInstance* FcitxInstanceCreate(sem_t *sem, int argc, char* argv[])
125 {
126     return FcitxInstanceCreateWithFD(sem, argc, argv, -1);
127 }
128 
129 FCITX_EXPORT_API
FcitxInstanceRun(int argc,char * argv[],int fd)130 boolean FcitxInstanceRun(int argc, char* argv[], int fd)
131 {
132     FcitxInstance* instance = fcitx_utils_new(FcitxInstance);
133 
134     do {
135         if (!ProcessOption(instance, argc, argv))
136             break;
137 
138         instance->fd = fd;
139 
140         RunInstance(instance);
141     } while(0);
142     boolean result = instance->loadingFatalError;
143 //    free(instance);
144 
145     return result;
146 }
147 
148 FCITX_EXPORT_API
FcitxInstanceCreatePause(sem_t * sem,int argc,char * argv[],int fd)149 FcitxInstance* FcitxInstanceCreatePause(sem_t *sem, int argc, char* argv[], int fd)
150 {
151     if (!sem) {
152         return NULL;
153     }
154 
155     FcitxInstance* instance = fcitx_utils_new(FcitxInstance);
156 
157     if (!ProcessOption(instance, argc, argv))
158         goto create_error_exit_1;
159 
160     instance->sem = sem;
161     instance->fd = fd;
162 
163     if (sem_init(&instance->startUpSem, 0, 0) != 0) {
164         goto create_error_exit_1;
165     }
166 
167     if (sem_init(&instance->notifySem, 0, 0) != 0) {
168         goto create_error_exit_2;
169     }
170 
171     if (pthread_create(&instance->pid, NULL, RunInstance, instance) != 0) {
172         goto create_error_exit_3;
173     }
174 
175     sem_wait(&instance->notifySem);
176 
177     return instance;
178 
179 create_error_exit_3:
180     sem_destroy(&instance->notifySem);
181 create_error_exit_2:
182     sem_destroy(&instance->startUpSem);
183 create_error_exit_1:
184     free(instance);
185     return NULL;
186 }
187 
188 FCITX_EXPORT_API
FcitxInstanceStart(FcitxInstance * instance)189 void FcitxInstanceStart(FcitxInstance* instance)
190 {
191     if (!instance->loadingFatalError) {
192         instance->initialized = true;
193 
194         if (sem_post(&instance->startUpSem))
195             instance->initialized = false;
196     }
197 }
198 
199 
200 FCITX_EXPORT_API
FcitxInstanceCreateWithFD(sem_t * sem,int argc,char * argv[],int fd)201 FcitxInstance* FcitxInstanceCreateWithFD(sem_t *sem, int argc, char* argv[], int fd)
202 {
203     FcitxInstance* instance = FcitxInstanceCreatePause(sem, argc, argv, fd);
204 
205     if (instance) {
206         FcitxInstanceStart(instance);
207     }
208 
209     return instance;
210 
211 }
212 
213 FCITX_EXPORT_API
FcitxInstanceSetRecheckEvent(FcitxInstance * instance)214 void FcitxInstanceSetRecheckEvent(FcitxInstance* instance)
215 {
216     instance->eventflag |= FEF_EVENT_CHECK;
217 }
218 
219 FCITX_EXPORT_API
220 jmp_buf FcitxRecover;
221 
RunInstance(void * arg)222 void* RunInstance(void* arg)
223 {
224     FcitxInstance* instance = (FcitxInstance*) arg;
225     FcitxAddonsInit(&instance->addons);
226     FcitxInstanceInitIM(instance);
227     FcitxInstanceInitNoPreeditApps(instance);
228     FcitxFrontendsInit(&instance->frontends);
229     InitFcitxModules(&instance->modules);
230     InitFcitxModules(&instance->eventmodules);
231     utarray_init(&instance->uistats, &stat_icd);
232     utarray_init(&instance->uicompstats, &compstat_icd);
233     utarray_init(&instance->uimenus, fcitx_ptr_icd);
234     utarray_init(&instance->timeout, &timeout_icd);
235     utarray_init(&instance->icdata, &icdata_icd);
236     instance->input = FcitxInputStateCreate();
237     instance->config = fcitx_utils_malloc0(sizeof(FcitxGlobalConfig));
238     instance->profile = fcitx_utils_malloc0(sizeof(FcitxProfile));
239     instance->globalIMName = strdup("");
240     if (instance->fd >= 0) {
241         fcntl(instance->fd, F_SETFL, O_NONBLOCK);
242     } else {
243         instance->fd = -1;
244     }
245 
246     if (!FcitxGlobalConfigLoad(instance->config))
247         goto error_exit;
248 
249     FcitxCandidateWordSetPageSize(instance->input->candList, instance->config->iMaxCandWord);
250 
251     int overrideDelay = instance->overrideDelay;
252 
253     if (overrideDelay < 0)
254         overrideDelay = instance->config->iDelayStart;
255 
256     if (overrideDelay > 0)
257         sleep(overrideDelay);
258 
259     instance->timeStart = time(NULL);
260     instance->globalState = instance->config->defaultIMState;
261     instance->totaltime = 0;
262 
263     FcitxInitThread(instance);
264     if (!FcitxProfileLoad(instance->profile, instance))
265         goto error_exit;
266     if (FcitxAddonGetConfigDesc() == NULL)
267         goto error_exit;
268     if (GetIMConfigDesc() == NULL)
269         goto error_exit;
270 
271     FcitxAddonsLoad(&instance->addons);
272     FcitxInstanceFillAddonOwner(instance, NULL);
273     FcitxInstanceResolveAddonDependency(instance);
274     FcitxInstanceInitBuiltInHotkey(instance);
275     FcitxInstanceInitBuiltContext(instance);
276     FcitxModuleLoad(instance);
277     if (instance->loadingFatalError)
278         return NULL;
279     if (!FcitxInstanceLoadAllIM(instance)) {
280         goto error_exit;
281     }
282 
283     FcitxInstanceInitIMMenu(instance);
284     FcitxUIRegisterMenu(instance, &instance->imMenu);
285     FcitxUIRegisterStatus(instance, instance, "remind",
286                            instance->profile->bUseRemind ? _("Use remind") :  _("No remind"),
287                           _("Toggle Remind"), ToggleRemindState, GetRemindEnabled);
288     FcitxUISetStatusVisable(instance, "remind",  false);
289 
290     FcitxUILoad(instance);
291 
292     instance->iIMIndex = FcitxInstanceGetIMIndexByName(instance, instance->profile->imName);
293     if (instance->iIMIndex < 0) {
294         instance->iIMIndex = 0;
295     }
296 
297     FcitxInstanceSwitchIMByIndex(instance, instance->iIMIndex);
298 
299     if (!FcitxInstanceLoadFrontend(instance)) {
300         goto error_exit;
301     }
302 
303     /* fcitx is running in a standalone thread or not */
304     if (instance->sem) {
305         sem_post(&instance->notifySem);
306         sem_wait(&instance->startUpSem);
307     } else {
308         instance->initialized = true;
309     }
310 
311     uint64_t curtime = 0;
312     while (1) {
313         FcitxAddon** pmodule;
314         uint8_t signo = 0;
315         if (instance->fd >= 0) {
316             while (read(instance->fd, &signo, sizeof(char)) > 0) {
317                 if (signo == SIGINT || signo == SIGTERM || signo == SIGQUIT || signo == SIGXCPU)
318                     FcitxInstanceEnd(instance);
319                 else if (signo == SIGHUP)
320                     FcitxInstanceRestart(instance);
321                 else if (signo == SIGUSR1)
322                     FcitxInstanceReloadConfig(instance);
323             }
324         }
325         do {
326             instance->eventflag &= (~FEF_PROCESS_EVENT_MASK);
327             for (pmodule = (FcitxAddon**) utarray_front(&instance->eventmodules);
328                   pmodule != NULL;
329                   pmodule = (FcitxAddon**) utarray_next(&instance->eventmodules, pmodule)) {
330                 FcitxModule* module = (*pmodule)->module;
331                 module->ProcessEvent((*pmodule)->addonInstance);
332             }
333             struct timeval current_time;
334             gettimeofday(&current_time, NULL);
335             curtime = (current_time.tv_sec * 1000LL) + (current_time.tv_usec / 1000LL);
336 
337             unsigned int idx = 0;
338             while(idx < utarray_len(&instance->timeout))
339             {
340                 TimeoutItem* ti = (TimeoutItem*) utarray_eltptr(&instance->timeout, idx);
341                 uint64_t id = ti->idx;
342                 if (ti->time + ti->milli <= curtime) {
343                     ti->callback(ti->arg);
344                     ti = (TimeoutItem*) utarray_eltptr(&instance->timeout, idx);
345                     /* faster remove */
346                     if (ti && ti->idx == id)
347                         utarray_remove_quick(&instance->timeout, idx);
348                     else {
349                         FcitxInstanceRemoveTimeoutById(instance, id);
350                         idx = 0;
351                     }
352                 }
353                 else {
354                     idx++;
355                 }
356             }
357 
358             if (instance->eventflag & FEF_UI_MOVE)
359                 FcitxUIMoveInputWindowReal(instance);
360 
361             if (instance->eventflag & FEF_UI_UPDATE)
362                 FcitxUIUpdateInputWindowReal(instance);
363         } while ((instance->eventflag & FEF_PROCESS_EVENT_MASK) != FEF_NONE);
364 
365         setjmp(FcitxRecover);
366 
367         if (instance->destroy || instance->restart) {
368             FcitxInstanceEnd(instance);
369             FcitxInstanceRealEnd(instance);
370             break;
371         }
372 
373         if (instance->eventflag & FEF_RELOAD_ADDON) {
374             instance->eventflag &= ~(FEF_RELOAD_ADDON);
375             FcitxInstanceReloadAddon(instance);
376         }
377 
378         FD_ZERO(&instance->rfds);
379         FD_ZERO(&instance->wfds);
380         FD_ZERO(&instance->efds);
381 
382         instance->maxfd = 0;
383         if (instance->fd > 0) {
384             instance->maxfd = instance->fd;
385             FD_SET(instance->fd, &instance->rfds);
386         }
387         for (pmodule = (FcitxAddon**) utarray_front(&instance->eventmodules);
388               pmodule != NULL;
389               pmodule = (FcitxAddon**) utarray_next(&instance->eventmodules, pmodule)) {
390             FcitxModule* module = (*pmodule)->module;
391             module->SetFD((*pmodule)->addonInstance);
392         }
393         if (instance->maxfd == 0)
394             break;
395         struct timeval tval;
396         struct timeval* ptval = NULL;
397         if (utarray_len(&instance->timeout) != 0) {
398             unsigned long int min_time = LONG_MAX;
399             TimeoutItem* ti;
400             for (ti = (TimeoutItem*)utarray_front(&instance->timeout);ti;
401                  ti = (TimeoutItem*)utarray_next(&instance->timeout, ti)) {
402                 if (ti->time + ti->milli - curtime < min_time) {
403                     min_time = ti->time + ti->milli - curtime;
404                 }
405             }
406             tval.tv_usec = (min_time % 1000) * 1000;
407             tval.tv_sec = min_time / 1000;
408             ptval = &tval;
409         }
410         select(instance->maxfd + 1, &instance->rfds, &instance->wfds,
411                &instance->efds, ptval);
412     }
413     if (instance->restart) {
414         fcitx_utils_restart_in_place();
415     }
416 
417     return NULL;
418 
419 error_exit:
420     if (instance->sem) {
421         sem_post(&instance->startUpSem);
422     }
423     FcitxInstanceEnd(instance);
424     return NULL;
425 }
426 
427 FCITX_EXPORT_API
FcitxInstanceRestart(FcitxInstance * instance)428 void FcitxInstanceRestart(FcitxInstance *instance)
429 {
430     instance->restart = true;
431 }
432 
433 FCITX_EXPORT_API
FcitxInstanceEnd(FcitxInstance * instance)434 void FcitxInstanceEnd(FcitxInstance* instance)
435 {
436     /* avoid duplicate destroy */
437     if (instance->destroy)
438         return;
439 
440     if (!instance->initialized) {
441         if (!instance->loadingFatalError) {
442             if (!instance->quietQuit)
443                 FcitxLog(ERROR, "Exiting.");
444             instance->loadingFatalError = true;
445 
446             if (instance->sem) {
447                 sem_post(instance->sem);
448             }
449         }
450         return;
451     }
452 
453     instance->destroy = true;
454 }
455 
456 FCITX_EXPORT_API
FcitxInstanceRealEnd(FcitxInstance * instance)457 void FcitxInstanceRealEnd(FcitxInstance* instance) {
458 
459     FcitxProfileSave(instance->profile);
460     FcitxInstanceSaveAllIM(instance);
461 
462     if (instance->uinormal && instance->uinormal->ui->Destroy)
463         instance->uinormal->ui->Destroy(instance->uinormal->addonInstance);
464 
465     if (instance->uifallback && instance->uifallback->ui->Destroy)
466         instance->uifallback->ui->Destroy(instance->uifallback->addonInstance);
467 
468     instance->uifallback = NULL;
469     instance->ui = NULL;
470     instance->uinormal = NULL;
471 
472     /* handle exit */
473     FcitxAddon** pimclass;
474     FcitxAddon** pfrontend;
475     FcitxFrontend* frontend;
476     FcitxInputContext* rec = NULL;
477 
478     for (pimclass = (FcitxAddon**)utarray_front(&instance->imeclasses);
479          pimclass != NULL;
480          pimclass = (FcitxAddon**)utarray_next(&instance->imeclasses, pimclass)
481         ) {
482         if ((*pimclass)->imclass->Destroy)
483             (*pimclass)->imclass->Destroy((*pimclass)->addonInstance);
484     }
485 
486     for (rec = instance->ic_list; rec != NULL; rec = rec->next) {
487         pfrontend = (FcitxAddon**)utarray_eltptr(&instance->frontends,
488                                                  (unsigned int)rec->frontendid);
489         frontend = (*pfrontend)->frontend;
490         frontend->CloseIM((*pfrontend)->addonInstance, rec);
491     }
492 
493     for (rec = instance->ic_list; rec != NULL; rec = rec->next) {
494         pfrontend = (FcitxAddon**)utarray_eltptr(&instance->frontends,
495                                                  (unsigned int)rec->frontendid);
496         frontend = (*pfrontend)->frontend;
497         frontend->DestroyIC((*pfrontend)->addonInstance, rec);
498     }
499 
500     for (pfrontend = (FcitxAddon**)utarray_front(&instance->frontends);
501          pfrontend != NULL;
502          pfrontend = (FcitxAddon**)utarray_next(&instance->frontends, pfrontend)
503         ) {
504         if (pfrontend == NULL)
505             continue;
506         FcitxFrontend* frontend = (*pfrontend)->frontend;
507         frontend->Destroy((*pfrontend)->addonInstance);
508     }
509 
510     FcitxAddon** pmodule;
511     for (pmodule = (FcitxAddon**) utarray_back(&instance->modules);
512          pmodule != NULL;
513          pmodule = (FcitxAddon**) utarray_prev(&instance->modules, pmodule)) {
514         if (pmodule == NULL)
515             return;
516         FcitxModule* module = (*pmodule)->module;
517         if (module->Destroy)
518             module->Destroy((*pmodule)->addonInstance);
519     }
520 
521     if (instance->sem) {
522         sem_post(instance->sem);
523     }
524 }
525 
FcitxInitThread(FcitxInstance * inst)526 void FcitxInitThread(FcitxInstance* inst)
527 {
528     int rc;
529     rc = pthread_mutex_init(&inst->fcitxMutex, NULL);
530     if (rc != 0)
531         FcitxLog(ERROR, _("pthread mutex init failed"));
532 }
533 
534 FCITX_EXPORT_API
FcitxInstanceLock(FcitxInstance * inst)535 int FcitxInstanceLock(FcitxInstance* inst)
536 {
537     if (inst->bMutexInited)
538         return pthread_mutex_lock(&inst->fcitxMutex);
539     return 0;
540 }
541 
542 FCITX_EXPORT_API
FcitxInstanceUnlock(FcitxInstance * inst)543 int FcitxInstanceUnlock(FcitxInstance* inst)
544 {
545     if (inst->bMutexInited)
546         return pthread_mutex_unlock(&inst->fcitxMutex);
547     return 0;
548 }
549 
ToggleRemindState(void * arg)550 void ToggleRemindState(void* arg)
551 {
552     FcitxInstance* instance = (FcitxInstance*) arg;
553     instance->profile->bUseRemind = !instance->profile->bUseRemind;
554     FcitxUISetStatusString(instance, "remind",
555                            instance->profile->bUseRemind ? _("Use remind") :  _("No remind"),
556                           _("Toggle Remind"));
557     FcitxProfileSave(instance->profile);
558 }
559 
GetRemindEnabled(void * arg)560 boolean GetRemindEnabled(void* arg)
561 {
562     FcitxInstance* instance = (FcitxInstance*) arg;
563     return instance->profile->bUseRemind;
564 }
565 
ProcessOption(FcitxInstance * instance,int argc,char * argv[])566 boolean ProcessOption(FcitxInstance* instance, int argc, char* argv[])
567 {
568     struct option longOptions[] = {
569         {"ui", 1, 0, 0},
570         {"replace", 0, 0, 0},
571         {"enable", 1, 0, 0},
572         {"disable", 1, 0, 0},
573         {"version", 0, 0, 0},
574         {"help", 0, 0, 0},
575         {NULL, 0, 0, 0}
576     };
577 
578     int optionIndex = 0;
579     int c;
580     char* uiname = NULL;
581     boolean runasdaemon = true;
582     int             overrideDelay = -1;
583     while ((c = getopt_long(argc, argv, "ru:dDs:hv", longOptions, &optionIndex)) != EOF) {
584         switch (c) {
585         case 0: {
586             switch (optionIndex) {
587             case 0:
588                 uiname = strdup(optarg);
589                 break;
590             case 1:
591                 instance->tryReplace = true;
592                 break;
593             case 2:
594                 {
595                     if (instance->enableList)
596                         fcitx_utils_free_string_list(instance->enableList);
597                     instance->enableList = fcitx_utils_split_string(optarg, ',');
598                 }
599                 break;
600             case 3:
601                 {
602                     if (instance->disableList)
603                         fcitx_utils_free_string_list(instance->disableList);
604                     instance->disableList = fcitx_utils_split_string(optarg, ',');
605                 }
606                 break;
607             case 4:
608                 instance->quietQuit = true;
609                 Version();
610                 return false;
611                 break;
612             default:
613                 instance->quietQuit = true;
614                 Usage();
615                 return false;
616             }
617         }
618         break;
619         case 'r':
620             instance->tryReplace = true;
621             break;
622         case 'u':
623             uiname = strdup(optarg);
624             break;
625         case 'd':
626             runasdaemon = true;
627             break;
628         case 'D':
629             runasdaemon = false;
630             break;
631         case 's':
632             overrideDelay = atoi(optarg);
633             break;
634         case 'h':
635             instance->quietQuit = true;
636             Usage();
637             return false;
638         case 'v':
639             instance->quietQuit = true;
640             Version();
641             return false;
642             break;
643         default:
644             instance->quietQuit = true;
645             Usage();
646             return false;
647         }
648     }
649 
650     if (uiname)
651         instance->uiname = uiname;
652     else
653         instance->uiname = NULL;
654 
655     if (runasdaemon)
656         fcitx_utils_init_as_daemon();
657 
658     instance->overrideDelay = overrideDelay;
659 
660     return true;
661 }
662 
663 FCITX_EXPORT_API
FcitxInstanceGetCurrentIC(FcitxInstance * instance)664 FcitxInputContext* FcitxInstanceGetCurrentIC(FcitxInstance* instance)
665 {
666     return instance->CurrentIC;
667 }
668 
669 FCITX_EXPORT_API
FcitxInstanceSetCurrentIC(FcitxInstance * instance,FcitxInputContext * ic)670 boolean FcitxInstanceSetCurrentIC(FcitxInstance* instance, FcitxInputContext* ic)
671 {
672     FcitxContextState prevstate = FcitxInstanceGetCurrentState(instance);
673     boolean changed = (instance->CurrentIC != ic);
674 
675     if (instance->CurrentIC) {
676         FcitxInstanceSetLastIC(instance, instance->CurrentIC);
677     }
678     instance->CurrentIC = ic;
679 
680     FcitxContextState nextstate = FcitxInstanceGetCurrentState(instance);
681 
682     if (!((prevstate == IS_CLOSED && nextstate == IS_CLOSED) || (prevstate != IS_CLOSED && nextstate != IS_CLOSED))) {
683         if (prevstate == IS_CLOSED)
684             instance->timeStart = time(NULL);
685         else
686             instance->totaltime += difftime(time(NULL), instance->timeStart);
687     }
688 
689     return changed;
690 }
691 
FcitxInstanceSetLastIC(FcitxInstance * instance,FcitxInputContext * ic)692 void FcitxInstanceSetLastIC(FcitxInstance* instance, FcitxInputContext* ic)
693 {
694     instance->lastIC = ic;
695 
696     free(instance->delayedIM);
697     instance->delayedIM = NULL;
698 }
699 
FcitxInstanceSetDelayedIM(FcitxInstance * instance,const char * delayedIM)700 void FcitxInstanceSetDelayedIM(FcitxInstance* instance, const char* delayedIM)
701 {
702     fcitx_utils_string_swap(&instance->delayedIM, delayedIM);
703 }
704 
705 
FcitxInstanceInitBuiltContext(FcitxInstance * instance)706 void FcitxInstanceInitBuiltContext(FcitxInstance* instance)
707 {
708     FcitxInstanceRegisterWatchableContext(instance, CONTEXT_ALTERNATIVE_PREVPAGE_KEY, FCT_Hotkey, FCF_ResetOnInputMethodChange);
709     FcitxInstanceRegisterWatchableContext(instance, CONTEXT_ALTERNATIVE_NEXTPAGE_KEY, FCT_Hotkey, FCF_ResetOnInputMethodChange);
710     FcitxInstanceRegisterWatchableContext(instance, CONTEXT_IM_KEYBOARD_LAYOUT, FCT_String, FCF_ResetOnInputMethodChange);
711     FcitxInstanceRegisterWatchableContext(instance, CONTEXT_IM_LANGUAGE, FCT_String, FCF_None);
712     FcitxInstanceRegisterWatchableContext(instance, CONTEXT_SHOW_REMIND_STATUS, FCT_Boolean, FCF_ResetOnInputMethodChange);
713     FcitxInstanceRegisterWatchableContext(instance, CONTEXT_DISABLE_AUTO_FIRST_CANDIDATE_HIGHTLIGHT, FCT_Boolean, FCF_ResetOnInputMethodChange);
714 
715     FcitxInstanceWatchContext(instance, CONTEXT_SHOW_REMIND_STATUS, FcitxInstanceShowRemindStatusChanged, instance);
716 }
717 
FcitxInstanceShowRemindStatusChanged(void * arg,const void * value)718 void FcitxInstanceShowRemindStatusChanged(void* arg, const void* value)
719 {
720     const boolean* b = value;
721     FcitxInstance* instance = arg;
722     FcitxUISetStatusVisable(instance, "remind",  *b);
723 }
724 
725 FCITX_EXPORT_API
FcitxInstanceIsTryReplace(FcitxInstance * instance)726 boolean FcitxInstanceIsTryReplace(FcitxInstance* instance)
727 {
728     return instance->tryReplace;
729 }
730 
731 FCITX_EXPORT_API
FcitxInstanceResetTryReplace(FcitxInstance * instance)732 void FcitxInstanceResetTryReplace(FcitxInstance* instance)
733 {
734     instance->tryReplace = false;
735 }
736 
737 FCITX_EXPORT_API
FcitxInstanceAddTimeout(FcitxInstance * instance,long int milli,FcitxTimeoutCallback callback,void * arg)738 uint64_t FcitxInstanceAddTimeout(FcitxInstance* instance, long int milli, FcitxTimeoutCallback callback , void* arg)
739 {
740     if (milli < 0)
741         return 0;
742 
743     struct timeval current_time;
744     gettimeofday(&current_time, NULL);
745     TimeoutItem item;
746     item.arg = arg;
747     item.callback =callback;
748     item.milli = milli;
749     item.idx = ++instance->timeoutIdx;
750     item.time = (current_time.tv_sec * 1000LL) + (current_time.tv_usec / 1000LL);
751     utarray_push_back(&instance->timeout, &item);
752 
753     return item.idx;
754 }
755 
756 FCITX_EXPORT_API
FcitxInstanceCheckTimeoutByFunc(FcitxInstance * instance,FcitxTimeoutCallback callback)757 boolean FcitxInstanceCheckTimeoutByFunc(FcitxInstance* instance, FcitxTimeoutCallback callback)
758 {
759     utarray_foreach(ti, &instance->timeout, TimeoutItem) {
760         if (ti->callback == callback)
761             return true;
762     }
763     return false;
764 }
765 
766 FCITX_EXPORT_API
FcitxInstanceCheckTimeoutById(FcitxInstance * instance,uint64_t id)767 boolean FcitxInstanceCheckTimeoutById(FcitxInstance *instance, uint64_t id)
768 {
769     utarray_foreach(ti, &instance->timeout, TimeoutItem) {
770         if (ti->idx == id)
771             return true;
772     }
773     return false;
774 }
775 
776 FCITX_EXPORT_API boolean
FcitxInstanceRemoveTimeoutByFunc(FcitxInstance * instance,FcitxTimeoutCallback callback)777 FcitxInstanceRemoveTimeoutByFunc(FcitxInstance* instance,
778                                  FcitxTimeoutCallback callback)
779 {
780     utarray_foreach(ti, &instance->timeout, TimeoutItem) {
781         if (ti->callback == callback) {
782             unsigned int idx = utarray_eltidx(&instance->timeout, ti);
783             utarray_remove_quick(&instance->timeout, idx);
784             return true;
785         }
786     }
787     return false;
788 }
789 
790 FCITX_EXPORT_API
FcitxInstanceRemoveTimeoutById(FcitxInstance * instance,uint64_t id)791 boolean FcitxInstanceRemoveTimeoutById(FcitxInstance* instance, uint64_t id)
792 {
793     if (id == 0)
794         return false;
795     utarray_foreach(ti, &instance->timeout, TimeoutItem) {
796         if (ti->idx == id) {
797             unsigned int idx = utarray_eltidx(&instance->timeout, ti);
798             utarray_remove_quick(&instance->timeout, idx);
799             return true;
800         }
801     }
802     return false;
803 }
804 
805 FCITX_EXPORT_API
FcitxInstanceWaitForEnd(FcitxInstance * instance)806 int FcitxInstanceWaitForEnd(FcitxInstance* instance) {
807     return pthread_join(instance->pid, NULL);
808 }
809 
FcitxInstanceInitNoPreeditApps(FcitxInstance * instance)810 static void FcitxInstanceInitNoPreeditApps(FcitxInstance* instance) {
811     UT_array* no_preedit_app_list = NULL;
812     const char* default_no_preedit_apps = NO_PREEDIT_APPS;
813     const char* no_preedit_apps;
814     UT_array* app_pat_list;
815     regex_t* re = NULL;
816 
817     no_preedit_apps = getenv("FCITX_NO_PREEDIT_APPS");
818     if (no_preedit_apps == NULL)
819         no_preedit_apps = default_no_preedit_apps;
820     app_pat_list = fcitx_utils_split_string(no_preedit_apps, ',');
821 
822     utarray_new(no_preedit_app_list, fcitx_ptr_icd);
823     utarray_foreach(pat, app_pat_list, char*) {
824         if (re == NULL)
825             re = malloc(sizeof(regex_t));
826         if (regcomp(re, *pat, REG_EXTENDED | REG_ICASE | REG_NOSUB))
827             continue;
828         utarray_push_back(no_preedit_app_list, &re);
829         re = NULL;
830     }
831 
832     fcitx_utils_free_string_list(app_pat_list);
833 
834     instance->no_preedit_app_list = no_preedit_app_list;
835 }
836 
837 // kate: indent-mode cstyle; space-indent on; indent-width 0;
838