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 <dlfcn.h>
22 #include <libintl.h>
23 #include <pthread.h>
24 #include <regex.h>
25 
26 #include "fcitx-utils/utarray.h"
27 #include "frontend.h"
28 #include "addon.h"
29 #include "ime-internal.h"
30 #include "fcitx-config/xdg.h"
31 #include "fcitx-utils/log.h"
32 #include "ui.h"
33 #include "hook.h"
34 #include "hook-internal.h"
35 #include "instance.h"
36 #include "instance-internal.h"
37 #include "addon-internal.h"
38 #include "config.h"
39 
40 static void FcitxInstanceCleanUpIC(FcitxInstance* instance);
41 static void NewICData(FcitxInstance* instance, FcitxInputContext* ic);
42 static void FreeICData(FcitxInstance* instance, FcitxInputContext* ic);
43 static void FillICData(FcitxInstance* instance, FcitxInputContext* ic);
44 static boolean AppPreeditBlacklisted(
45     FcitxInstance* instance, FcitxInputContext* ic);
46 
FillICData(FcitxInstance * instance,FcitxInputContext * ic)47 void FillICData(FcitxInstance* instance, FcitxInputContext* ic)
48 {
49     FcitxInputContext2* ic2 = (FcitxInputContext2*) ic;
50     unsigned int i = utarray_len(ic2->data);
51     for (;i < utarray_len(&instance->icdata);i++) {
52         FcitxICDataInfo *info =
53             (FcitxICDataInfo*)_utarray_eltptr(&instance->icdata, i);
54         void *data = NULL;
55         if (info->allocCallback)
56             data = info->allocCallback(info->arg);
57         utarray_push_back(ic2->data, &data);
58     }
59 }
60 
61 
NewICData(FcitxInstance * instance,FcitxInputContext * ic)62 void NewICData(FcitxInstance* instance, FcitxInputContext* ic)
63 {
64     FcitxInputContext2* ic2 = (FcitxInputContext2*) ic;
65     utarray_new(ic2->data, fcitx_ptr_icd);
66     FillICData(instance, ic);
67 }
68 
FreeICData(FcitxInstance * instance,FcitxInputContext * ic)69 void FreeICData(FcitxInstance* instance, FcitxInputContext* ic)
70 {
71     FcitxInputContext2* ic2 = (FcitxInputContext2*) ic;
72     unsigned int i = 0;
73     for (;i < utarray_len(ic2->data);i++) {
74         void** data = (void**)_utarray_eltptr(ic2->data, i);
75         FcitxICDataInfo* info =
76             (FcitxICDataInfo*)_utarray_eltptr(&instance->icdata, i);
77         if (info->freeCallback) {
78             info->freeCallback(info->arg, *data);
79         }
80     }
81     utarray_free(ic2->data);
82     fcitx_utils_free(ic2->prgname);
83 }
84 
85 FCITX_EXPORT_API void*
FcitxInstanceGetICData(FcitxInstance * instance,FcitxInputContext * ic,int icdataid)86 FcitxInstanceGetICData(FcitxInstance *instance, FcitxInputContext *ic,
87                        int icdataid)
88 {
89     FCITX_UNUSED(instance);
90     if (!ic)
91         return NULL;
92 
93     FcitxInputContext2* ic2 = (FcitxInputContext2*)ic;
94     void **data = fcitx_array_eltptr(ic2->data, icdataid);
95     if (!data)
96         return NULL;
97     return *data;
98 }
99 
100 static
FcitxInstanceSetICDataInternal(struct _FcitxInstance * instance,FcitxInputContext * ic,int icdataid,void * newdata,boolean copy)101 void FcitxInstanceSetICDataInternal(struct _FcitxInstance* instance, FcitxInputContext* ic, int icdataid, void* newdata, boolean copy)
102 {
103     FcitxInputContext2* ic2 = (FcitxInputContext2*) ic;
104     FcitxICDataInfo *info = fcitx_array_eltptr(&instance->icdata, icdataid);
105     void **data = fcitx_array_eltptr(ic2->data, icdataid);
106     if (!data || !info)
107         return;
108     if (copy) {
109         if (info->copyCallback) {
110             *data = info->copyCallback(info->arg, *data, newdata);
111         }
112     }
113     else
114         *data = newdata;
115 }
116 
117 FCITX_EXPORT_API
FcitxInstanceSetICData(struct _FcitxInstance * instance,FcitxInputContext * ic,int icdataid,void * newdata)118 void FcitxInstanceSetICData(struct _FcitxInstance* instance, FcitxInputContext* ic, int icdataid, void* newdata)
119 {
120     if (!ic)
121         return;
122     switch (instance->config->shareState) {
123     case ShareState_All:
124     case ShareState_PerProgram: {
125         FcitxInputContext *rec = instance->ic_list;
126         while (rec != NULL) {
127             boolean flag = false;
128             if (instance->config->shareState == ShareState_All)
129                 flag = true;
130             else {
131                 flag = FcitxInstanceCheckICFromSameApplication(instance, rec, ic);
132             }
133 
134             if (flag)
135                 FcitxInstanceSetICDataInternal(instance, rec, icdataid, newdata, (rec != ic));
136             rec = rec->next;
137         }
138     }
139     break;
140     case ShareState_None:
141         FcitxInstanceSetICDataInternal(instance, ic, icdataid, newdata, false);
142         break;
143     }
144 }
145 
146 FCITX_EXPORT_API
FcitxFrontendsInit(UT_array * frontends)147 void FcitxFrontendsInit(UT_array* frontends)
148 {
149     utarray_init(frontends, fcitx_ptr_icd);
150 }
151 
152 FCITX_EXPORT_API FcitxInputContext*
FcitxInstanceCreateIC(FcitxInstance * instance,int frontendid,void * priv)153 FcitxInstanceCreateIC(FcitxInstance* instance, int frontendid, void * priv)
154 {
155     /* clean up invalid ic here */
156     FcitxInstanceCleanUpIC(instance);
157     FcitxAddon **pfrontend = FcitxInstanceGetPFrontend(instance, frontendid);
158     if (pfrontend == NULL)
159         return NULL;
160     FcitxFrontend* frontend = (*pfrontend)->frontend;
161 
162     FcitxInputContext *rec;
163     if (instance->free_list != NULL) {
164         rec = instance->free_list;
165         instance->free_list = instance->free_list->next;
166     } else
167         rec = malloc(sizeof(FcitxInputContext2));
168 
169     memset(rec, 0, sizeof(FcitxInputContext2));
170     rec->frontendid = frontendid;
171     rec->offset_x = -1;
172     rec->offset_y = -1;
173     ((FcitxInputContext2*)rec)->prgname = NULL;
174     ((FcitxInputContext2*)rec)->mayUsePreedit = Tri_Unknown;
175 
176     NewICData(instance, rec);
177     switch (instance->config->shareState) {
178     case ShareState_All:
179         rec->state = instance->globalState;
180         break;
181     case ShareState_None:
182     case ShareState_PerProgram:
183         rec->state = instance->config->defaultIMState;
184         break;
185     default:
186         break;
187     }
188 
189     frontend->CreateIC((*pfrontend)->addonInstance, rec, priv);
190 
191     rec->next = instance->ic_list;
192     instance->ic_list = rec;
193     return rec;
194 }
195 
196 FCITX_EXPORT_API
FcitxInstanceAllocDataForIC(FcitxInstance * instance,FcitxICDataAllocCallback allocCallback,FcitxICDataCopyCallback copyCallback,FcitxICDataFreeCallback freeCallback,void * arg)197 int FcitxInstanceAllocDataForIC(FcitxInstance* instance,
198                                 FcitxICDataAllocCallback allocCallback,
199                                 FcitxICDataCopyCallback copyCallback,
200                                 FcitxICDataFreeCallback freeCallback, void* arg)
201 {
202     FcitxICDataInfo info;
203     info.allocCallback = allocCallback;
204     info.copyCallback = copyCallback;
205     info.freeCallback = freeCallback;
206     info.arg = arg;
207 
208     utarray_push_back(&instance->icdata, &info);
209     FcitxInputContext *rec = instance->ic_list;
210     while (rec) {
211         FillICData(instance, rec);
212         rec = rec->next;
213     }
214     return utarray_len(&instance->icdata) - 1;
215 }
216 
FcitxInstanceCleanUpIC(FcitxInstance * instance)217 void FcitxInstanceCleanUpIC(FcitxInstance* instance)
218 {
219     FcitxInputContext *rec = instance->ic_list, *last = NULL, *todel;
220 
221     while (rec) {
222         FcitxAddon **pfrontend = FcitxInstanceGetPFrontend(instance,
223                                                            rec->frontendid);
224         FcitxFrontend *frontend = (*pfrontend)->frontend;
225         pid_t pid = 0;
226         if (frontend->GetPid)
227             pid = frontend->GetPid((*pfrontend)->addonInstance, rec);
228         if (pid && !fcitx_utils_pid_exists(pid)) {
229             if (last != NULL)
230                 last->next = rec->next;
231             else
232                 instance->ic_list = rec->next;
233             todel = rec;
234             rec = rec->next;
235             todel->next = instance->free_list;
236             instance->free_list = todel;
237             frontend->DestroyIC((*pfrontend)->addonInstance, todel);
238             FreeICData(instance, todel);
239 
240             if (todel == instance->lastIC) {
241                 FcitxInstanceSetLastIC(instance, NULL);
242             }
243 
244             if (todel == instance->CurrentIC) {
245                 instance->CurrentIC = NULL;
246                 FcitxUICloseInputWindow(instance);
247                 FcitxUIOnInputUnFocus(instance);
248                 FcitxInstanceSetCurrentIC(instance, NULL);
249             }
250         }
251         else {
252             last = rec;
253             rec = rec->next;
254         }
255     }
256 }
257 
258 FCITX_EXPORT_API
FcitxInstanceFindIC(FcitxInstance * instance,int frontendid,void * filter)259 FcitxInputContext* FcitxInstanceFindIC(FcitxInstance* instance, int frontendid, void *filter)
260 {
261     FcitxAddon **pfrontend = FcitxInstanceGetPFrontend(instance, frontendid);
262     if (pfrontend == NULL)
263         return NULL;
264     FcitxFrontend* frontend = (*pfrontend)->frontend;
265     FcitxInputContext *rec = instance->ic_list;
266     while (rec != NULL) {
267         if (rec->frontendid == frontendid && frontend->CheckIC((*pfrontend)->addonInstance, rec, filter))
268             return rec;
269         rec = rec->next;
270     }
271     return NULL;
272 }
273 
274 FCITX_EXPORT_API
FcitxInstanceSetICStateFromSameApplication(FcitxInstance * instance,int frontendid,FcitxInputContext * ic)275 void FcitxInstanceSetICStateFromSameApplication(FcitxInstance* instance, int frontendid, FcitxInputContext *ic)
276 {
277     FcitxAddon **pfrontend = FcitxInstanceGetPFrontend(instance, frontendid);
278     if (pfrontend == NULL)
279         return;
280     FcitxFrontend* frontend = (*pfrontend)->frontend;
281     if (!frontend->CheckICFromSameApplication)
282         return;
283     FcitxInputContext *rec = instance->ic_list;
284     while (rec != NULL) {
285         if (rec->frontendid == frontendid && frontend->CheckICFromSameApplication((*pfrontend)->addonInstance, rec, ic)) {
286             ic->state = rec->state;
287             break;
288         }
289         rec = rec->next;
290     }
291 }
292 
293 FCITX_EXPORT_API
FcitxInstanceDestroyIC(FcitxInstance * instance,int frontendid,void * filter)294 void FcitxInstanceDestroyIC(FcitxInstance* instance, int frontendid, void* filter)
295 {
296     FcitxInputContext *rec, *last;
297     FcitxAddon **pfrontend = FcitxInstanceGetPFrontend(instance, frontendid);
298     if (pfrontend == NULL)
299         return;
300     FcitxFrontend* frontend = (*pfrontend)->frontend;
301 
302     last = NULL;
303 
304     for (rec = instance->ic_list; rec != NULL; last = rec, rec = rec->next) {
305         if (rec->frontendid == frontendid && frontend->CheckIC((*pfrontend)->addonInstance, rec, filter)) {
306             if (last != NULL)
307                 last->next = rec->next;
308             else
309                 instance->ic_list = rec->next;
310 
311             rec->next = instance->free_list;
312             instance->free_list = rec;
313 
314             if (rec == instance->lastIC) {
315                 FcitxInstanceSetLastIC(instance, NULL);
316             }
317 
318             if (rec == FcitxInstanceGetCurrentIC(instance)) {
319                 FcitxUICloseInputWindow(instance);
320                 FcitxUIOnInputUnFocus(instance);
321                 FcitxInstanceSetCurrentIC(instance, NULL);
322             }
323 
324             frontend->DestroyIC((*pfrontend)->addonInstance, rec);
325             FreeICData(instance, rec);
326             return;
327         }
328     }
329 
330     return;
331 }
332 
333 FCITX_EXPORT_API
FcitxInstanceGetCurrentState(FcitxInstance * instance)334 FcitxContextState FcitxInstanceGetCurrentState(FcitxInstance* instance)
335 {
336     if (instance->CurrentIC)
337         return instance->CurrentIC->state;
338     else
339         return IS_CLOSED;
340 }
341 
342 FCITX_EXPORT_API
FcitxInstanceGetCurrentStatev2(FcitxInstance * instance)343 FcitxContextState FcitxInstanceGetCurrentStatev2(FcitxInstance* instance)
344 {
345     if (instance->CurrentIC) {
346         if (instance->CurrentIC->state == IS_INACTIVE)
347             return IS_ACTIVE;
348         return instance->CurrentIC->state;
349     }
350     else
351         return IS_CLOSED;
352 }
353 
354 FCITX_EXPORT_API
FcitxInstanceGetCurrentCapacity(FcitxInstance * instance)355 FcitxCapacityFlags FcitxInstanceGetCurrentCapacity(FcitxInstance* instance)
356 {
357     if (instance->CurrentIC)
358         return instance->CurrentIC->contextCaps;
359     else
360         return CAPACITY_NONE;
361 }
362 
363 FCITX_EXPORT_API
FcitxInstanceCommitString(FcitxInstance * instance,FcitxInputContext * ic,const char * str)364 void FcitxInstanceCommitString(FcitxInstance* instance, FcitxInputContext* ic, const char* str)
365 {
366     if (str == NULL)
367         return ;
368 
369     if (ic == NULL)
370         return;
371 
372     char *pstr = FcitxInstanceProcessCommitFilter(instance, str);
373     if (pstr != NULL)
374         str = pstr;
375 
376     FcitxAddon **pfrontend = FcitxInstanceGetPFrontend(instance, ic->frontendid);
377     if (pfrontend == NULL)
378         return;
379     FcitxFrontend* frontend = (*pfrontend)->frontend;
380     frontend->CommitString((*pfrontend)->addonInstance, ic, str);
381 
382     FcitxInputState* input = instance->input;
383     fcitx_utf8_strncpy(input->strLastCommit, str, MAX_USER_INPUT);
384     input->strLastCommit[MAX_USER_INPUT] = '\0';
385     instance->iHZInputed += (int)(fcitx_utf8_strlen(str));
386 
387     if (pstr)
388         free(pstr);
389 }
390 
391 FCITX_EXPORT_API
FcitxInstanceGetSurroundingText(FcitxInstance * instance,FcitxInputContext * ic,char ** str,unsigned int * cursor,unsigned int * anchor)392 boolean FcitxInstanceGetSurroundingText(FcitxInstance* instance, FcitxInputContext* ic, char** str, unsigned int* cursor, unsigned int* anchor)
393 {
394     if (ic == NULL)
395         return false;
396 
397     if (!(ic->contextCaps & CAPACITY_SURROUNDING_TEXT))
398         return false;
399     FcitxAddon **pfrontend = FcitxInstanceGetPFrontend(instance, ic->frontendid);
400     if (pfrontend == NULL)
401         return false;
402 
403     FcitxFrontend* frontend = (*pfrontend)->frontend;
404     if (frontend->GetSurroundingPreedit) {
405         return frontend->GetSurroundingPreedit((*pfrontend)->addonInstance, ic, str, cursor, anchor);
406     }
407     return false;
408 }
409 
410 FCITX_EXPORT_API
FcitxInstanceDeleteSurroundingText(FcitxInstance * instance,FcitxInputContext * ic,int offset,unsigned int size)411 void FcitxInstanceDeleteSurroundingText(FcitxInstance* instance, FcitxInputContext* ic, int offset, unsigned int size)
412 {
413     if (ic == NULL)
414         return;
415     FcitxAddon **pfrontend = FcitxInstanceGetPFrontend(instance, ic->frontendid);
416     if (pfrontend == NULL)
417         return;
418 
419     FcitxFrontend* frontend = (*pfrontend)->frontend;
420     if (frontend->DeleteSurroundingText) {
421         frontend->DeleteSurroundingText((*pfrontend)->addonInstance, ic, offset, size);
422     }
423 }
424 
425 FCITX_EXPORT_API
FcitxInstanceUpdatePreedit(FcitxInstance * instance,FcitxInputContext * ic)426 void FcitxInstanceUpdatePreedit(FcitxInstance* instance, FcitxInputContext* ic)
427 {
428     if (!instance->profile->bUsePreedit)
429         return;
430 
431     if (ic == NULL)
432         return;
433 
434     if (AppPreeditBlacklisted(instance, ic))
435         return;
436 
437     if (!(ic->contextCaps & CAPACITY_PREEDIT))
438         return;
439     FcitxAddon **pfrontend = FcitxInstanceGetPFrontend(instance, ic->frontendid);
440     if (pfrontend == NULL)
441         return;
442     FcitxFrontend* frontend = (*pfrontend)->frontend;
443     frontend->UpdatePreedit((*pfrontend)->addonInstance, ic);
444 }
445 
446 FCITX_EXPORT_API
FcitxInstanceUpdateClientSideUI(FcitxInstance * instance,FcitxInputContext * ic)447 void FcitxInstanceUpdateClientSideUI(FcitxInstance* instance, FcitxInputContext* ic)
448 {
449     if (ic == NULL)
450         return;
451 
452     if (!(ic->contextCaps & CAPACITY_CLIENT_SIDE_UI))
453         return;
454     FcitxAddon **pfrontend = FcitxInstanceGetPFrontend(instance, ic->frontendid);
455     if (pfrontend == NULL)
456         return;
457     FcitxFrontend* frontend = (*pfrontend)->frontend;
458     if (frontend->UpdateClientSideUI)
459         frontend->UpdateClientSideUI((*pfrontend)->addonInstance, ic);
460 }
461 
462 FCITX_EXPORT_API
FcitxInstanceSetWindowOffset(FcitxInstance * instance,FcitxInputContext * ic,int x,int y)463 void FcitxInstanceSetWindowOffset(FcitxInstance* instance, FcitxInputContext *ic, int x, int y)
464 {
465     FcitxAddon **pfrontend = FcitxInstanceGetPFrontend(instance, ic->frontendid);
466     if (pfrontend == NULL)
467         return;
468     FcitxFrontend* frontend = (*pfrontend)->frontend;
469     if (frontend->SetWindowOffset)
470         frontend->SetWindowOffset((*pfrontend)->addonInstance, ic, x, y);
471 }
472 
473 FCITX_EXPORT_API
FcitxInstanceGetWindowPosition(FcitxInstance * instance,FcitxInputContext * ic,int * x,int * y)474 void FcitxInstanceGetWindowPosition(FcitxInstance* instance, FcitxInputContext* ic, int* x, int* y)
475 {
476     if (ic == NULL)
477         return;
478 
479     FcitxAddon **pfrontend = FcitxInstanceGetPFrontend(instance, ic->frontendid);
480     if (pfrontend == NULL)
481         return;
482     FcitxFrontend* frontend = (*pfrontend)->frontend;
483     int rx, ry, rw, rh;
484     if (frontend->GetWindowRect) {
485         frontend->GetWindowRect((*pfrontend)->addonInstance, ic,
486                                 &rx, &ry, &rw, &rh);
487         *x = rx;
488         *y = ry + rh;
489     }
490 }
491 
492 FCITX_EXPORT_API
FcitxInstanceGetWindowRect(FcitxInstance * instance,FcitxInputContext * ic,int * x,int * y,int * w,int * h)493 void FcitxInstanceGetWindowRect(FcitxInstance* instance, FcitxInputContext* ic, int* x, int* y, int* w, int* h)
494 {
495     if (ic == NULL)
496         return;
497 
498     FcitxAddon **pfrontend = FcitxInstanceGetPFrontend(instance, ic->frontendid);
499     if (pfrontend == NULL)
500         return;
501     FcitxFrontend* frontend = (*pfrontend)->frontend;
502     if (frontend->GetWindowRect) {
503         frontend->GetWindowRect((*pfrontend)->addonInstance, ic, x, y, w, h);
504     }
505 }
506 
507 FCITX_EXPORT_API
FcitxInstanceLoadFrontend(FcitxInstance * instance)508 boolean FcitxInstanceLoadFrontend(FcitxInstance* instance)
509 {
510     UT_array* addons = &instance->addons;
511     UT_array* frontends = &instance->frontends;
512     FcitxAddon *addon;
513     int frontendindex = 0;
514     utarray_clear(frontends);
515     for (addon = (FcitxAddon *) utarray_front(addons);
516             addon != NULL;
517             addon = (FcitxAddon *) utarray_next(addons, addon)) {
518         if (addon->bEnabled && addon->category == AC_FRONTEND) {
519             char *modulePath;
520             switch (addon->type) {
521             case AT_SHAREDLIBRARY: {
522                 FILE *fp = FcitxXDGGetLibFile(addon->library, "r", &modulePath);
523                 void *handle;
524                 FcitxFrontend* frontend;
525                 if (!fp)
526                     break;
527                 fclose(fp);
528                 handle = dlopen(modulePath, RTLD_NOW | RTLD_NODELETE | (addon->loadLocal ? RTLD_LOCAL : RTLD_GLOBAL));
529                 if (!handle) {
530                     FcitxLog(ERROR, _("Frontend: open %s fail %s") , modulePath , dlerror());
531                     break;
532                 }
533 
534                 if (!FcitxCheckABIVersion(handle, addon->name)) {
535                     FcitxLog(ERROR, "%s ABI Version Error", addon->name);
536                     dlclose(handle);
537                     break;
538                 }
539 
540                 frontend = FcitxGetSymbol(handle, addon->name, "frontend");
541                 if (!frontend || !frontend->Create) {
542                     FcitxLog(ERROR, _("Frontend: bad frontend"));
543                     dlclose(handle);
544                     break;
545                 }
546                 if ((addon->addonInstance = frontend->Create(instance, frontendindex)) == NULL) {
547                     dlclose(handle);
548                     break;
549                 }
550                 if (instance->loadingFatalError)
551                     return false;
552                 addon->frontend = frontend;
553                 frontendindex ++;
554                 utarray_push_back(frontends, &addon);
555             }
556             break;
557             default:
558                 break;
559             }
560             free(modulePath);
561         }
562     }
563 
564     if (utarray_len(&instance->frontends) <= 0) {
565         FcitxLog(ERROR, _("No available frontend"));
566         return false;
567     }
568     return true;
569 }
570 
571 FCITX_EXPORT_API
FcitxInstanceICSupportPreedit(FcitxInstance * instance,FcitxInputContext * ic)572 boolean FcitxInstanceICSupportPreedit(FcitxInstance* instance, FcitxInputContext* ic)
573 {
574     if (!ic || ((ic->contextCaps & CAPACITY_PREEDIT) == 0
575                 || !instance->profile->bUsePreedit
576                 || AppPreeditBlacklisted(instance, ic)))
577         return false;
578     return true;
579 }
580 
AppPreeditBlacklisted(FcitxInstance * instance,FcitxInputContext * ic)581 static boolean AppPreeditBlacklisted(
582     FcitxInstance* instance, FcitxInputContext* ic)
583 {
584     FcitxInputContext2* ic2 = (FcitxInputContext2*) ic;
585     if (ic2->mayUsePreedit != Tri_Unknown)
586         return ic2->mayUsePreedit;
587 
588     ic2->mayUsePreedit = false;
589 
590     const char* prgname = ic2->prgname;
591     if (!prgname)
592         return false;
593 
594     utarray_foreach(re, instance->no_preedit_app_list, regex_t*) {
595         if (regexec(*re, prgname, 0, NULL, 0) == 0) {
596             /* matched */
597             ic2->mayUsePreedit = true;
598             return true;
599         }
600     }
601 
602     return false;
603 }
604 
605 // kate: indent-mode cstyle; space-indent on; indent-width 0;
606