1 /***************************************************************************
2  *   Copyright (C) 2010~2012 by CSSlayer                                   *
3  *                                                                         *
4  *   This program is free software; you can redistribute it and/or modify  *
5  *   it under the terms of the GNU General Public License as published by  *
6  *   the Free Software Foundation; either version 2 of the License, or     *
7  *   (at your option) any later version.                                   *
8  *                                                                         *
9  *   This program is distributed in the hope that it will be useful,       *
10  *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
11  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
12  *   GNU General Public License for more details.                          *
13  *                                                                         *
14  *   You should have received a copy of the GNU General Public License     *
15  *   along with this program; if not, write to the                         *
16  *   Free Software Foundation, Inc.,                                       *
17  *   51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.              *
18  ***************************************************************************/
19 
20 #include <limits.h>
21 #include <dbus/dbus.h>
22 
23 #include "fcitx/fcitx.h"
24 #include "fcitx/frontend.h"
25 #include "fcitx-utils/utils.h"
26 #include "module/dbus/fcitx-dbus.h"
27 #include "module/dbusstuff/property.h"
28 #include "fcitx/instance.h"
29 #include "fcitx/module.h"
30 #include "fcitx-utils/log.h"
31 #include "fcitx/configfile.h"
32 #include "fcitx/hook.h"
33 #include "ipc.h"
34 
35 #define GetIPCIC(ic) ((FcitxIPCIC*) (ic)->privateic)
36 
37 typedef struct _FcitxIPCCreateICPriv {
38     DBusMessage* message;
39     DBusConnection* conn;
40 } FcitxIPCCreateICPriv;
41 
42 typedef struct _FcitxLastSentIMInfo
43 {
44     char* name;
45     char* uniqueName;
46     char* langCode;
47 } FcitxLastSentIMInfo;
48 
49 typedef struct _FcitxIPCIC {
50     int id;
51     char* sender;
52     char path[32];
53     int width;
54     int height;
55     pid_t pid;
56     char* surroundingText;
57     unsigned int anchor;
58     unsigned int cursor;
59     boolean lastPreeditIsEmpty;
60     boolean isPriv;
61     FcitxLastSentIMInfo lastSentIMInfo;
62 } FcitxIPCIC;
63 
64 typedef struct _FcitxIPCFrontend {
65     int frontendid;
66     int maxid;
67     DBusConnection* _conn;
68     DBusConnection* _privconn;
69     FcitxInstance* owner;
70 } FcitxIPCFrontend;
71 
72 typedef struct _FcitxIPCKeyEvent {
73     FcitxKeySym sym;
74     unsigned int state;
75 } FcitxIPCKeyEvent;
76 
77 static void* IPCCreate(FcitxInstance* instance, int frontendid);
78 static boolean IPCDestroy(void* arg);
79 void IPCCreateIC(void* arg, FcitxInputContext* context, void *priv);
80 boolean IPCCheckIC(void* arg, FcitxInputContext* context, void* priv);
81 void IPCDestroyIC(void* arg, FcitxInputContext* context);
82 static void IPCEnableIM(void* arg, FcitxInputContext* ic);
83 static void IPCCloseIM(void* arg, FcitxInputContext* ic);
84 static void IPCCommitString(void* arg, FcitxInputContext* ic, const char* str);
85 static void IPCForwardKey(void* arg, FcitxInputContext* ic, FcitxKeyEventType event, FcitxKeySym sym, unsigned int state);
86 static void IPCSetWindowOffset(void* arg, FcitxInputContext* ic, int x, int y);
87 static void IPCGetWindowRect(void* arg, FcitxInputContext* ic, int* x, int* y, int* w, int* h);
88 static void IPCUpdatePreedit(void* arg, FcitxInputContext* ic);
89 static void IPCDeleteSurroundingText(void* arg, FcitxInputContext* ic, int offset, unsigned int size);
90 static boolean IPCGetSurroundingText(void* arg, FcitxInputContext* ic, char** str, unsigned int* cursor, unsigned int* anchor);
91 static void IPCUpdateClientSideUI(void* arg, FcitxInputContext* ic);
92 static DBusHandlerResult IPCDBusEventHandler(DBusConnection *connection, DBusMessage *message, void *user_data);
93 static DBusHandlerResult IPCICDBusEventHandler(DBusConnection *connection, DBusMessage *msg, void *user_data);
94 static void IPCICFocusIn(FcitxIPCFrontend* ipc, FcitxInputContext* ic);
95 static void IPCICFocusOut(FcitxIPCFrontend* ipc, FcitxInputContext* ic);
96 static void IPCICReset(FcitxIPCFrontend* ipc, FcitxInputContext* ic);
97 static void IPCICSetCursorRect(FcitxIPCFrontend* ipc, FcitxInputContext* ic, int x, int y, int w, int h);
98 static int IPCProcessKey(FcitxIPCFrontend* ipc, FcitxInputContext* callic, const uint32_t originsym, const uint32_t keycode, const uint32_t originstate, uint32_t t, FcitxKeyEventType type);
99 static boolean IPCCheckICFromSameApplication(void* arg, FcitxInputContext* icToCheck, FcitxInputContext* ic);
100 static void IPCEmitPropertiesChanged(void* arg, const char* const* properties);
101 static void IPCEmitPropertyChanged(void* arg, const char* property);
102 static void IPCGetPropertyIMList(void* arg, DBusMessageIter* iter);
103 static void IPCSetPropertyIMList(void* arg, DBusMessageIter* iter);
104 static void IPCUpdateIMList(void* arg);
105 static void IPCGetPropertyCurrentIM(void* arg, DBusMessageIter* iter);
106 static void IPCSetPropertyCurrentIM(void* arg, DBusMessageIter* iter);
107 static void IPCUpdateCurrentIM(void* arg);
108 static void IPCUpdateIMInfoForIC(void* arg);
109 static pid_t IPCGetPid(void* arg, FcitxInputContext* ic);
110 
111 const FcitxDBusPropertyTable propertTable[] = {
112     { FCITX_IM_DBUS_INTERFACE, "IMList", "a(sssb)", IPCGetPropertyIMList, IPCSetPropertyIMList },
113     { FCITX_IM_DBUS_INTERFACE, "CurrentIM", "s", IPCGetPropertyCurrentIM, IPCSetPropertyCurrentIM },
114     { NULL, NULL, NULL, NULL, NULL }
115 };
116 
117 const char * im_introspection_xml =
118     "<!DOCTYPE node PUBLIC \"-//freedesktop//DTD D-BUS Object Introspection 1.0//EN\" "
119     "\"http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd\">"
120     "<node>"
121     "<interface name=\"" DBUS_INTERFACE_INTROSPECTABLE "\">"
122     "<method name=\"Introspect\">"
123     "<arg name=\"data\" direction=\"out\" type=\"s\"/>"
124     "</method>"
125     "</interface>"
126     "<interface name=\"" DBUS_INTERFACE_PROPERTIES "\">"
127     "<method name=\"Get\">"
128     "<arg name=\"interface_name\" direction=\"in\" type=\"s\"/>"
129     "<arg name=\"property_name\" direction=\"in\" type=\"s\"/>"
130     "<arg name=\"value\" direction=\"out\" type=\"v\"/>"
131     "</method>"
132     "<method name=\"Set\">"
133     "<arg name=\"interface_name\" direction=\"in\" type=\"s\"/>"
134     "<arg name=\"property_name\" direction=\"in\" type=\"s\"/>"
135     "<arg name=\"value\" direction=\"in\" type=\"v\"/>"
136     "</method>"
137     "<method name=\"GetAll\">"
138     "<arg name=\"interface_name\" direction=\"in\" type=\"s\"/>"
139     "<arg name=\"values\" direction=\"out\" type=\"a{sv}\"/>"
140     "</method>"
141     "<signal name=\"PropertiesChanged\">"
142     "<arg name=\"interface_name\" type=\"s\"/>"
143     "<arg name=\"changed_properties\" type=\"a{sv}\"/>"
144     "<arg name=\"invalidated_properties\" type=\"as\"/>"
145     "</signal>"
146     "</interface>"
147     "<interface name=\"" FCITX_IM_DBUS_INTERFACE "\">"
148     "<method name=\"CreateIC\">"
149     "<arg name=\"icid\" direction=\"out\" type=\"i\"/>"
150     "<arg name=\"keyval1\" direction=\"out\" type=\"u\"/>"
151     "<arg name=\"state1\" direction=\"out\" type=\"u\"/>"
152     "<arg name=\"keyval2\" direction=\"out\" type=\"u\"/>"
153     "<arg name=\"state2\" direction=\"out\" type=\"u\"/>"
154     "</method>"
155     "<method name=\"CreateICv2\">"
156     "<arg name=\"appname\" direction=\"in\" type=\"s\"/>"
157     "<arg name=\"icid\" direction=\"out\" type=\"i\"/>"
158     "<arg name=\"enable\" direction=\"out\" type=\"b\"/>"
159     "<arg name=\"keyval1\" direction=\"out\" type=\"u\"/>"
160     "<arg name=\"state1\" direction=\"out\" type=\"u\"/>"
161     "<arg name=\"keyval2\" direction=\"out\" type=\"u\"/>"
162     "<arg name=\"state2\" direction=\"out\" type=\"u\"/>"
163     "</method>"
164     "<method name=\"CreateICv3\">"
165     "<arg name=\"appname\" direction=\"in\" type=\"s\"/>"
166     "<arg name=\"pid\" direction=\"in\" type=\"i\"/>"
167     "<arg name=\"icid\" direction=\"out\" type=\"i\"/>"
168     "<arg name=\"enable\" direction=\"out\" type=\"b\"/>"
169     "<arg name=\"keyval1\" direction=\"out\" type=\"u\"/>"
170     "<arg name=\"state1\" direction=\"out\" type=\"u\"/>"
171     "<arg name=\"keyval2\" direction=\"out\" type=\"u\"/>"
172     "<arg name=\"state2\" direction=\"out\" type=\"u\"/>"
173     "</method>"
174     "<method name=\"Exit\">"
175     "</method>"
176     "<method name=\"GetCurrentIM\">"
177     "<arg name=\"im\" direction=\"out\" type=\"s\"/>"
178     "</method>"
179     "<method name=\"SetCurrentIM\">"
180     "<arg name=\"im\" direction=\"in\" type=\"s\"/>"
181     "</method>"
182     "<method name=\"ReloadConfig\">"
183     "</method>"
184     "<method name=\"ReloadAddonConfig\">"
185     "<arg name=\"addon\" direction=\"in\" type=\"s\"/>"
186     "</method>"
187     "<method name=\"Restart\">"
188     "</method>"
189     "<method name=\"Configure\">"
190     "</method>"
191     "<method name=\"ConfigureAddon\">"
192     "<arg name=\"addon\" direction=\"in\" type=\"s\"/>"
193     "</method>"
194     "<method name=\"ConfigureIM\">"
195     "<arg name=\"im\" direction=\"in\" type=\"s\"/>"
196     "</method>"
197     "<method name=\"GetCurrentUI\">"
198     "<arg name=\"addon\" direction=\"out\" type=\"s\"/>"
199     "</method>"
200     "<method name=\"GetIMAddon\">"
201     "<arg name=\"im\" direction=\"in\" type=\"s\"/>"
202     "<arg name=\"addon\" direction=\"out\" type=\"s\"/>"
203     "</method>"
204     "<method name=\"ActivateIM\">"
205     "</method>"
206     "<method name=\"InactivateIM\">"
207     "</method>"
208     "<method name=\"ToggleIM\">"
209     "</method>"
210     "<method name=\"ResetIMList\">"
211     "</method>"
212     "<method name=\"GetCurrentState\">"
213     "<arg name=\"state\" direction=\"out\" type=\"i\"/>"
214     "</method>"
215     "<property access=\"readwrite\" type=\"a(sssb)\" name=\"IMList\">"
216     "<annotation name=\"org.freedesktop.DBus.Property.EmitsChangedSignal\" value=\"true\"/>"
217     "</property>"
218     "<property access=\"readwrite\" type=\"s\" name=\"CurrentIM\">"
219     "<annotation name=\"org.freedesktop.DBus.Property.EmitsChangedSignal\" value=\"true\"/>"
220     "</property>"
221     "</interface>"
222     "</node>";
223 
224 const char * ic_introspection_xml =
225     "<!DOCTYPE node PUBLIC \"-//freedesktop//DTD D-BUS Object Introspection 1.0//EN\" "
226     "\"http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd\">"
227     "<node>"
228     "<interface name=\"org.freedesktop.DBus.Introspectable\">"
229     "<method name=\"Introspect\">"
230     "<arg name=\"data\" direction=\"out\" type=\"s\"/>"
231     "</method>"
232     "</interface>"
233     "<interface name=\"" FCITX_IC_DBUS_INTERFACE "\">"
234     "<method name=\"EnableIC\">"
235     "</method>"
236     "<method name=\"CloseIC\">"
237     "</method>"
238     "<method name=\"FocusIn\">"
239     "</method>"
240     "<method name=\"FocusOut\">"
241     "</method>"
242     "<method name=\"Reset\">"
243     "</method>"
244     "<method name=\"CommitPreedit\">"
245     "</method>"
246     "<method name=\"MouseEvent\">"
247     "<arg name=\"x\" direction=\"in\" type=\"i\"/>"
248     "</method>"
249     "<method name=\"SetCursorLocation\">"
250     "<arg name=\"x\" direction=\"in\" type=\"i\"/>"
251     "<arg name=\"y\" direction=\"in\" type=\"i\"/>"
252     "</method>"
253     "<method name=\"SetCursorRect\">"
254     "<arg name=\"x\" direction=\"in\" type=\"i\"/>"
255     "<arg name=\"y\" direction=\"in\" type=\"i\"/>"
256     "<arg name=\"w\" direction=\"in\" type=\"i\"/>"
257     "<arg name=\"h\" direction=\"in\" type=\"i\"/>"
258     "</method>"
259     "<method name=\"SetCapacity\">"
260     "<arg name=\"caps\" direction=\"in\" type=\"u\"/>"
261     "</method>"
262     "<method name=\"SetSurroundingText\">"
263     "<arg name=\"text\" direction=\"in\" type=\"s\"/>"
264     "<arg name=\"cursor\" direction=\"in\" type=\"u\"/>"
265     "<arg name=\"anchor\" direction=\"in\" type=\"u\"/>"
266     "</method>"
267     "<method name=\"SetSurroundingTextPosition\">"
268     "<arg name=\"cursor\" direction=\"in\" type=\"u\"/>"
269     "<arg name=\"anchor\" direction=\"in\" type=\"u\"/>"
270     "</method>"
271     "<method name=\"DestroyIC\">"
272     "</method>"
273     "<method name=\"ProcessKeyEvent\">"
274     "<arg name=\"keyval\" direction=\"in\" type=\"u\"/>"
275     "<arg name=\"keycode\" direction=\"in\" type=\"u\"/>"
276     "<arg name=\"state\" direction=\"in\" type=\"u\"/>"
277     "<arg name=\"type\" direction=\"in\" type=\"i\"/>"
278     "<arg name=\"time\" direction=\"in\" type=\"u\"/>"
279     "<arg name=\"ret\" direction=\"out\" type=\"i\"/>"
280     "</method>"
281     "<signal name=\"EnableIM\">"
282     "</signal>"
283     "<signal name=\"CloseIM\">"
284     "</signal>"
285     "<signal name=\"CommitString\">"
286     "<arg name=\"str\" type=\"s\"/>"
287     "</signal>"
288     "<signal name=\"CurrentIM\">"
289     "<arg name=\"name\" type=\"s\"/>"
290     "<arg name=\"uniqueName\" type=\"s\"/>"
291     "<arg name=\"langCode\" type=\"s\"/>"
292     "</signal>"
293     "<signal name=\"UpdatePreedit\">"
294     "<arg name=\"str\" type=\"s\"/>"
295     "<arg name=\"cursorpos\" type=\"i\"/>"
296     "</signal>"
297     "<signal name=\"DeleteSurroundingText\">"
298     "<arg name=\"offset\" type=\"i\"/>"
299     "<arg name=\"nchar\" type=\"u\"/>"
300     "</signal>"
301     "<signal name=\"UpdateFormattedPreedit\">"
302     "<arg name=\"str\" type=\"a(si)\"/>"
303     "<arg name=\"cursorpos\" type=\"i\"/>"
304     "</signal>"
305     "<signal name=\"UpdateClientSideUI\">"
306     "<arg name=\"auxup\" type=\"s\"/>"
307     "<arg name=\"auxdown\" type=\"s\"/>"
308     "<arg name=\"preedit\" type=\"s\"/>"
309     "<arg name=\"candidateword\" type=\"s\"/>"
310     "<arg name=\"imname\" type=\"s\"/>"
311     "<arg name=\"cursorpos\" type=\"i\"/>"
312     "</signal>"
313     "<signal name=\"ForwardKey\">"
314     "<arg name=\"keyval\" type=\"u\"/>"
315     "<arg name=\"state\" type=\"u\"/>"
316     "<arg name=\"type\" type=\"i\"/>"
317     "</signal>"
318     "</interface>"
319     "</node>";
320 
321 FCITX_DEFINE_PLUGIN(fcitx_ipc, frontend, FcitxFrontend) = {
322     IPCCreate,
323     IPCDestroy,
324     IPCCreateIC,
325     IPCCheckIC,
326     IPCDestroyIC,
327     IPCEnableIM,
328     IPCCloseIM,
329     IPCCommitString,
330     IPCForwardKey,
331     IPCSetWindowOffset,
332     IPCGetWindowRect,
333     IPCUpdatePreedit,
334     IPCUpdateClientSideUI,
335     NULL,
336     IPCCheckICFromSameApplication,
337     IPCGetPid,
338     IPCDeleteSurroundingText,
339     IPCGetSurroundingText
340 };
341 
IPCCreate(FcitxInstance * instance,int frontendid)342 void* IPCCreate(FcitxInstance* instance, int frontendid)
343 {
344     FcitxIPCFrontend* ipc = fcitx_utils_malloc0(sizeof(FcitxIPCFrontend));
345     ipc->frontendid = frontendid;
346     ipc->owner = instance;
347 
348     ipc->_conn = FcitxDBusGetConnection(instance);
349     ipc->_privconn = FcitxDBusGetPrivConnection(instance);
350 
351     if (ipc->_conn == NULL && ipc->_privconn == NULL) {
352         FcitxLog(ERROR, "DBus Not initialized");
353         free(ipc);
354         return NULL;
355     }
356 
357 
358     DBusObjectPathVTable fcitxIPCVTable = {NULL, &IPCDBusEventHandler, NULL, NULL, NULL, NULL };
359 
360     if (ipc->_conn) {
361         dbus_connection_register_object_path(ipc->_conn,  FCITX_IM_DBUS_PATH, &fcitxIPCVTable, ipc);
362     }
363 
364     if (ipc->_privconn) {
365         dbus_connection_register_object_path(ipc->_privconn, FCITX_IM_DBUS_PATH, &fcitxIPCVTable, ipc);
366     }
367 
368     FcitxIMEventHook hook;
369     hook.arg = ipc;
370     hook.func = IPCUpdateIMList;
371     FcitxInstanceRegisterUpdateIMListHook(instance, hook);
372     hook.func = IPCUpdateCurrentIM;
373     FcitxInstanceRegisterIMChangedHook(instance, hook);
374     hook.func = IPCUpdateIMInfoForIC;
375     FcitxInstanceRegisterInputFocusHook(instance, hook);
376 
377     return ipc;
378 }
379 
IPCDestroy(void * arg)380 boolean IPCDestroy(void* arg)
381 {
382     FCITX_UNUSED(arg);
383     return true;
384 }
385 
IPCCreateIC(void * arg,FcitxInputContext * context,void * priv)386 void IPCCreateIC(void* arg, FcitxInputContext* context, void* priv)
387 {
388     FcitxIPCFrontend* ipc = (FcitxIPCFrontend*) arg;
389     FcitxIPCIC* ipcic = fcitx_utils_new(FcitxIPCIC);
390     FcitxIPCCreateICPriv* ipcpriv = (FcitxIPCCreateICPriv*) priv;
391     DBusMessage* message = ipcpriv->message;
392     DBusMessage *reply = dbus_message_new_method_return(message);
393     FcitxGlobalConfig* config = FcitxInstanceGetGlobalConfig(ipc->owner);
394 
395     context->privateic = ipcic;
396 
397     ipcic->id = ipc->maxid;
398     ipcic->sender = strdup(dbus_message_get_sender(message));
399     ipc->maxid ++;
400     ipcic->lastPreeditIsEmpty = false;
401     ipcic->isPriv = (ipcpriv->conn != ipc->_conn);
402     sprintf(ipcic->path, FCITX_IC_DBUS_PATH, ipcic->id);
403 
404     uint32_t arg1, arg2, arg3, arg4;
405     arg1 = config->hkTrigger[0].sym;
406     arg2 = config->hkTrigger[0].state;
407     arg3 = config->hkTrigger[1].sym;
408     arg4 = config->hkTrigger[1].state;
409     if (dbus_message_is_method_call(message, FCITX_IM_DBUS_INTERFACE, "CreateIC")) {
410         /* CreateIC v1 indicates that default state can only be disabled */
411         context->state = IS_CLOSED;
412         context->contextCaps |= CAPACITY_CLIENT_SIDE_CONTROL_STATE;
413         dbus_message_append_args(reply,
414                                  DBUS_TYPE_INT32, &ipcic->id,
415                                  DBUS_TYPE_UINT32, &arg1,
416                                  DBUS_TYPE_UINT32, &arg2,
417                                  DBUS_TYPE_UINT32, &arg3,
418                                  DBUS_TYPE_UINT32, &arg4,
419                                  DBUS_TYPE_INVALID);
420     } else if (dbus_message_is_method_call(message, FCITX_IM_DBUS_INTERFACE, "CreateICv2")) {
421 
422         DBusError error;
423         dbus_error_init(&error);
424         char* appname = NULL;
425         if (!dbus_message_get_args(message, &error, DBUS_TYPE_STRING, &appname, DBUS_TYPE_INVALID))
426             appname = NULL;
427         else {
428             if (strlen(appname) == 0) {
429                 appname = NULL;
430             } else {
431                 appname = strdup(appname);
432             }
433         }
434         ((FcitxInputContext2*)context)->prgname = appname;
435 
436         if (config->shareState == ShareState_PerProgram) {
437             FcitxInstanceSetICStateFromSameApplication(ipc->owner, ipc->frontendid, context);
438         }
439 
440         boolean arg0 = context->state != IS_CLOSED;
441 
442         dbus_error_free(&error);
443         dbus_message_append_args(reply,
444                                  DBUS_TYPE_INT32, &ipcic->id,
445                                  DBUS_TYPE_BOOLEAN, &arg0,
446                                  DBUS_TYPE_UINT32, &arg1,
447                                  DBUS_TYPE_UINT32, &arg2,
448                                  DBUS_TYPE_UINT32, &arg3,
449                                  DBUS_TYPE_UINT32, &arg4,
450                                  DBUS_TYPE_INVALID);
451     } else if (dbus_message_is_method_call(message, FCITX_IM_DBUS_INTERFACE, "CreateICv3")) {
452 
453         DBusError error;
454         dbus_error_init(&error);
455         int icpid = 0;
456         char* appname = NULL;
457         if (!dbus_message_get_args(message, &error, DBUS_TYPE_STRING, &appname, DBUS_TYPE_INT32, &icpid, DBUS_TYPE_INVALID))
458             appname = NULL;
459         else {
460             if (strlen(appname) == 0) {
461                 appname = NULL;
462             } else {
463                 appname = strdup(appname);
464             }
465         }
466         ipcic->pid = icpid;
467         ((FcitxInputContext2*)context)->prgname = appname;
468 
469         if (config->shareState == ShareState_PerProgram) {
470             FcitxInstanceSetICStateFromSameApplication(ipc->owner, ipc->frontendid, context);
471         }
472 
473         boolean arg0 = context->state != IS_CLOSED;
474 
475         dbus_error_free(&error);
476         dbus_message_append_args(reply,
477                                  DBUS_TYPE_INT32, &ipcic->id,
478                                  DBUS_TYPE_BOOLEAN, &arg0,
479                                  DBUS_TYPE_UINT32, &arg1,
480                                  DBUS_TYPE_UINT32, &arg2,
481                                  DBUS_TYPE_UINT32, &arg3,
482                                  DBUS_TYPE_UINT32, &arg4,
483                                  DBUS_TYPE_INVALID);
484     }
485     dbus_connection_send(ipcpriv->conn, reply, NULL);
486     dbus_message_unref(reply);
487 
488     DBusObjectPathVTable vtable = {NULL, &IPCICDBusEventHandler, NULL, NULL, NULL, NULL };
489     if (!ipcic->isPriv) {
490         if (ipc->_conn) {
491             dbus_connection_register_object_path(ipc->_conn, ipcic->path, &vtable, ipc);
492             dbus_connection_flush(ipc->_conn);
493         }
494     }
495     else {
496         if (ipc->_privconn) {
497             dbus_connection_register_object_path(ipc->_privconn, ipcic->path, &vtable, ipc);
498             dbus_connection_flush(ipc->_privconn);
499         }
500     }
501 }
502 
IPCCheckIC(void * arg,FcitxInputContext * context,void * priv)503 boolean IPCCheckIC(void* arg, FcitxInputContext* context, void* priv)
504 {
505     FCITX_UNUSED(arg);
506     int *id = (int*)priv;
507     if (GetIPCIC(context)->id == *id)
508         return true;
509     return false;
510 }
511 
IPCDestroyIC(void * arg,FcitxInputContext * context)512 void IPCDestroyIC(void* arg, FcitxInputContext* context)
513 {
514     FcitxIPCFrontend* ipc = (FcitxIPCFrontend*) arg;
515     FcitxIPCIC* ipcic = GetIPCIC(context);
516 
517     if (!ipcic->isPriv) {
518         if (ipc->_conn)
519             dbus_connection_unregister_object_path(ipc->_conn, GetIPCIC(context)->path);
520     }
521     else {
522         if (ipc->_privconn)
523             dbus_connection_unregister_object_path(ipc->_privconn, GetIPCIC(context)->path);
524     }
525     fcitx_utils_free(ipcic->lastSentIMInfo.name);
526     fcitx_utils_free(ipcic->lastSentIMInfo.uniqueName);
527     fcitx_utils_free(ipcic->lastSentIMInfo.langCode);
528     fcitx_utils_free(ipcic->surroundingText);
529     fcitx_utils_free(ipcic->sender);
530     free(context->privateic);
531     context->privateic = NULL;
532 }
533 
IPCSendSignal(FcitxIPCFrontend * ipc,FcitxIPCIC * ipcic,DBusMessage * msg)534 void IPCSendSignal(FcitxIPCFrontend* ipc, FcitxIPCIC* ipcic, DBusMessage* msg)
535 {
536     if (!ipcic || !ipcic->isPriv) {
537         if (ipc->_conn) {
538             dbus_connection_send(ipc->_conn, msg, NULL);
539             dbus_connection_flush(ipc->_conn);
540         }
541     }
542     if (!ipcic || ipcic->isPriv) {
543         if (ipc->_privconn) {
544             dbus_connection_send(ipc->_privconn, msg, NULL);
545             dbus_connection_flush(ipc->_privconn);
546         }
547     }
548     dbus_message_unref(msg);
549 }
550 
IPCEnableIM(void * arg,FcitxInputContext * ic)551 void IPCEnableIM(void* arg, FcitxInputContext* ic)
552 {
553     FcitxIPCFrontend* ipc = (FcitxIPCFrontend*) arg;
554     DBusMessage* msg = dbus_message_new_signal(GetIPCIC(ic)->path, // object name of the signal
555                        FCITX_IC_DBUS_INTERFACE, // interface name of the signal
556                        "EnableIM"); // name of the signal
557 
558     IPCSendSignal(ipc, GetIPCIC(ic), msg);
559 }
560 
IPCCloseIM(void * arg,FcitxInputContext * ic)561 void IPCCloseIM(void* arg, FcitxInputContext* ic)
562 {
563     FcitxIPCFrontend* ipc = (FcitxIPCFrontend*) arg;
564     DBusMessage* msg = dbus_message_new_signal(GetIPCIC(ic)->path, // object name of the signal
565                        FCITX_IC_DBUS_INTERFACE, // interface name of the signal
566                        "CloseIM"); // name of the signal
567     IPCSendSignal(ipc, GetIPCIC(ic), msg);
568 }
569 
IPCCommitString(void * arg,FcitxInputContext * ic,const char * str)570 void IPCCommitString(void* arg, FcitxInputContext* ic, const char* str)
571 {
572     FcitxIPCFrontend* ipc = (FcitxIPCFrontend*) arg;
573 
574     if (!fcitx_utf8_check_string(str))
575         return;
576     DBusMessage* msg = dbus_message_new_signal(GetIPCIC(ic)->path, // object name of the signal
577                        FCITX_IC_DBUS_INTERFACE, // interface name of the signal
578                        "CommitString"); // name of the signal
579 
580     dbus_message_append_args(msg, DBUS_TYPE_STRING, &str, DBUS_TYPE_INVALID);
581     IPCSendSignal(ipc, GetIPCIC(ic), msg);
582 }
583 
IPCForwardKey(void * arg,FcitxInputContext * ic,FcitxKeyEventType event,FcitxKeySym sym,unsigned int state)584 void IPCForwardKey(void* arg, FcitxInputContext* ic, FcitxKeyEventType event, FcitxKeySym sym, unsigned int state)
585 {
586     FcitxIPCFrontend* ipc = (FcitxIPCFrontend*) arg;
587     DBusMessage* msg = dbus_message_new_signal(GetIPCIC(ic)->path, // object name of the signal
588                        FCITX_IC_DBUS_INTERFACE, // interface name of the signal
589                        "ForwardKey"); // name of the signal
590 
591     uint32_t keyval = (uint32_t) sym;
592     uint32_t keystate = (uint32_t) state;
593     int32_t type = (int) event;
594     dbus_message_append_args(msg, DBUS_TYPE_UINT32, &keyval, DBUS_TYPE_UINT32, &keystate, DBUS_TYPE_INT32, &type, DBUS_TYPE_INVALID);
595     IPCSendSignal(ipc, GetIPCIC(ic), msg);
596 }
597 
IPCSetWindowOffset(void * arg,FcitxInputContext * ic,int x,int y)598 void IPCSetWindowOffset(void* arg, FcitxInputContext* ic, int x, int y)
599 {
600     FCITX_UNUSED(arg);
601     ic->offset_x = x;
602     ic->offset_y = y - GetIPCIC(ic)->height;
603 }
604 
IPCGetWindowRect(void * arg,FcitxInputContext * ic,int * x,int * y,int * w,int * h)605 void IPCGetWindowRect(void* arg, FcitxInputContext* ic, int* x, int* y, int* w, int* h)
606 {
607     FCITX_UNUSED(arg);
608     *x = ic->offset_x;
609     *y = ic->offset_y;
610     *w = GetIPCIC(ic)->width;
611     *h = GetIPCIC(ic)->height;
612 }
613 
IPCDBusEventHandler(DBusConnection * connection,DBusMessage * msg,void * user_data)614 static DBusHandlerResult IPCDBusEventHandler(DBusConnection *connection, DBusMessage *msg, void *user_data)
615 {
616     FcitxIPCFrontend* ipc = (FcitxIPCFrontend*) user_data;
617     FcitxInstance* instance = ipc->owner;
618     DBusMessage* reply = NULL;
619     boolean flush = true;
620     DBusHandlerResult result = DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
621 
622     if (dbus_message_is_method_call(msg, DBUS_INTERFACE_INTROSPECTABLE, "Introspect")) {
623         reply = dbus_message_new_method_return(msg);
624 
625         dbus_message_append_args(reply, DBUS_TYPE_STRING, &im_introspection_xml, DBUS_TYPE_INVALID);
626     } else if (dbus_message_is_method_call(msg, DBUS_INTERFACE_PROPERTIES, "Get")) {
627         reply = FcitxDBusPropertyGet(ipc, propertTable, msg);
628     } else if (dbus_message_is_method_call(msg, DBUS_INTERFACE_PROPERTIES, "Set")) {
629         reply = FcitxDBusPropertySet(ipc, propertTable, msg);
630     } else if (dbus_message_is_method_call(msg, DBUS_INTERFACE_PROPERTIES, "GetAll")) {
631         reply = FcitxDBusPropertyGetAll(ipc, propertTable, msg);
632     } else if (dbus_message_is_method_call(msg, FCITX_IM_DBUS_INTERFACE, "CreateIC")
633             || dbus_message_is_method_call(msg, FCITX_IM_DBUS_INTERFACE, "CreateICv2")
634             || dbus_message_is_method_call(msg, FCITX_IM_DBUS_INTERFACE, "CreateICv3")
635             ) {
636         /* we have no choice here, so just return */
637         FcitxIPCCreateICPriv ipcpriv;
638         ipcpriv.message = msg;
639         ipcpriv.conn = connection;
640         FcitxInstanceCreateIC(ipc->owner, ipc->frontendid, &ipcpriv);
641         return DBUS_HANDLER_RESULT_HANDLED;
642     } else if (dbus_message_is_method_call(msg, FCITX_IM_DBUS_INTERFACE, "Exit")) {
643         FcitxLog(INFO, "Receive message ask for quit");
644         reply = dbus_message_new_method_return(msg);
645         dbus_connection_send(connection, reply, NULL);
646         dbus_message_unref(reply);
647         dbus_connection_flush(connection);
648         FcitxInstanceEnd(instance);
649         return DBUS_HANDLER_RESULT_HANDLED;
650     } else if (dbus_message_is_method_call(msg, FCITX_IM_DBUS_INTERFACE, "GetCurrentIM")) {
651         reply = dbus_message_new_method_return(msg);
652         FcitxIM* im = FcitxInstanceGetCurrentIM(ipc->owner);
653         const char* name = "";
654         if (im) {
655             name = im->uniqueName;
656         }
657         dbus_message_append_args(reply, DBUS_TYPE_STRING, &name, DBUS_TYPE_INVALID);
658     } else if (dbus_message_is_method_call(msg, FCITX_IM_DBUS_INTERFACE, "SetCurrentIM")) {
659         DBusError error;
660         dbus_error_init(&error);
661         char* imname = NULL;
662         if (dbus_message_get_args(msg, &error, DBUS_TYPE_STRING, &imname, DBUS_TYPE_INVALID)) {
663             FcitxInstanceSwitchIMByName(instance, imname);
664             reply = dbus_message_new_method_return(msg);
665         } else {
666             reply = FcitxDBusPropertyUnknownMethod(msg);
667         }
668         dbus_error_free(&error);
669     } else if (dbus_message_is_method_call(msg, FCITX_IM_DBUS_INTERFACE, "GetIMAddon")) {
670         DBusError error;
671         dbus_error_init(&error);
672         char* imname = NULL;
673         if (dbus_message_get_args(msg, &error, DBUS_TYPE_STRING, &imname, DBUS_TYPE_INVALID)) {
674             FcitxIM* im = FcitxInstanceGetIMFromIMList(instance, IMAS_Disable, imname);
675             const char* name = "";
676             if (im && im->owner)
677                 name = im->owner->name;
678             reply = dbus_message_new_method_return(msg);
679             dbus_message_append_args(reply, DBUS_TYPE_STRING, &name, DBUS_TYPE_INVALID);
680         } else {
681             reply = FcitxDBusPropertyUnknownMethod(msg);
682         }
683         dbus_error_free(&error);
684     } else if (dbus_message_is_method_call(msg, FCITX_IM_DBUS_INTERFACE, "GetCurrentUI")) {
685         const char* name = "";
686         FcitxAddon* uiaddon  = FcitxInstanceGetCurrentUI(instance);
687         if (uiaddon) {
688             name = uiaddon->name;
689         }
690         reply = dbus_message_new_method_return(msg);
691         dbus_message_append_args(reply, DBUS_TYPE_STRING, &name, DBUS_TYPE_INVALID);
692     } else if (dbus_message_is_method_call(msg, FCITX_IM_DBUS_INTERFACE, "Configure")) {
693         reply = dbus_message_new_method_return(msg);
694         fcitx_utils_launch_configure_tool();
695     } else if (dbus_message_is_method_call(msg, FCITX_IM_DBUS_INTERFACE, "ActivateIM")) {
696         FcitxInstanceEnableIM(ipc->owner, FcitxInstanceGetCurrentIC(ipc->owner), false);
697         reply = dbus_message_new_method_return(msg);
698     } else if (dbus_message_is_method_call(msg, FCITX_IM_DBUS_INTERFACE, "InactivateIM")) {
699         FcitxInstanceCloseIM(ipc->owner, FcitxInstanceGetCurrentIC(ipc->owner));
700         reply = dbus_message_new_method_return(msg);
701     } else if (dbus_message_is_method_call(msg, FCITX_IM_DBUS_INTERFACE, "ToggleIM")) {
702         FcitxInstanceChangeIMState(ipc->owner, FcitxInstanceGetCurrentIC(ipc->owner));
703         reply = dbus_message_new_method_return(msg);
704     } else if (dbus_message_is_method_call(msg, FCITX_IM_DBUS_INTERFACE, "ResetIMList")) {
705         FcitxProfile* profile = FcitxInstanceGetProfile(instance);
706         if (profile->imList)
707             free(profile->imList);
708         profile->imList = strdup("");;
709         FcitxInstanceUpdateIMList(instance);
710         reply = dbus_message_new_method_return(msg);
711     } else if (dbus_message_is_method_call(msg, FCITX_IM_DBUS_INTERFACE, "GetCurrentState")) {
712         int r = FcitxInstanceGetCurrentState(ipc->owner);
713         reply = dbus_message_new_method_return(msg);
714         dbus_message_append_args(reply,
715                                  DBUS_TYPE_INT32, &r,
716                                  DBUS_TYPE_INVALID);
717     } else if (dbus_message_is_method_call(msg, FCITX_IM_DBUS_INTERFACE, "ConfigureAddon")) {
718         DBusError error;
719         dbus_error_init(&error);
720         char* addonname = NULL;
721         if (dbus_message_get_args(msg, &error, DBUS_TYPE_STRING, &addonname, DBUS_TYPE_INVALID)) {
722             if (addonname) {
723                 fcitx_utils_launch_configure_tool_for_addon(addonname);
724             }
725             reply = dbus_message_new_method_return(msg);
726         } else {
727             reply = FcitxDBusPropertyUnknownMethod(msg);
728         }
729         dbus_error_free(&error);
730     } else if (dbus_message_is_method_call(msg, FCITX_IM_DBUS_INTERFACE, "ConfigureIM")) {
731         DBusError error;
732         dbus_error_init(&error);
733         char* imname = NULL;
734         if (dbus_message_get_args(msg, &error, DBUS_TYPE_STRING, &imname, DBUS_TYPE_INVALID)) {
735             if (imname) {
736                 fcitx_utils_launch_configure_tool_for_addon(imname);
737             }
738             reply = dbus_message_new_method_return(msg);
739         } else {
740             reply = FcitxDBusPropertyUnknownMethod(msg);
741         }
742         dbus_error_free(&error);
743     } else if (dbus_message_is_method_call(msg, FCITX_IM_DBUS_INTERFACE, "ReloadAddonConfig")) {
744         DBusError error;
745         dbus_error_init(&error);
746         char* addonname = NULL;
747         if (dbus_message_get_args(msg, &error, DBUS_TYPE_STRING, &addonname, DBUS_TYPE_INVALID)) {
748             if (addonname) {
749                 FcitxInstanceReloadAddonConfig(instance, addonname);
750             }
751             reply = dbus_message_new_method_return(msg);
752         } else {
753             reply = FcitxDBusPropertyUnknownMethod(msg);
754         }
755         dbus_error_free(&error);
756     } else if (dbus_message_is_method_call(msg, FCITX_IM_DBUS_INTERFACE, "ReloadConfig")) {
757         FcitxInstanceReloadConfig(instance);
758         reply = dbus_message_new_method_return(msg);
759         flush = true;
760     } else if (dbus_message_is_method_call(msg, FCITX_IM_DBUS_INTERFACE, "Restart")) {
761         FcitxInstanceRestart(instance);
762         reply = dbus_message_new_method_return(msg);
763         flush = true;
764     }
765 
766     if (reply) {
767         dbus_connection_send(connection, reply, NULL);
768         dbus_message_unref(reply);
769         if (flush) {
770             dbus_connection_flush(connection);
771         }
772         result = DBUS_HANDLER_RESULT_HANDLED;
773     }
774 
775     return result;
776 }
777 
778 
IPCICDBusEventHandler(DBusConnection * connection,DBusMessage * msg,void * user_data)779 static DBusHandlerResult IPCICDBusEventHandler(DBusConnection *connection, DBusMessage *msg, void *user_data)
780 {
781     FcitxIPCFrontend* ipc = (FcitxIPCFrontend*) user_data;
782     int id = -1;
783     sscanf(dbus_message_get_path(msg), FCITX_IC_DBUS_PATH, &id);
784     FcitxInputContext* ic = FcitxInstanceFindIC(ipc->owner, ipc->frontendid, &id);
785     DBusHandlerResult result = DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
786     DBusMessage *reply = NULL;
787     boolean flush = false;
788 
789     if (dbus_message_is_method_call(msg, DBUS_INTERFACE_INTROSPECTABLE, "Introspect")) {
790         reply = dbus_message_new_method_return(msg);
791         dbus_message_append_args(reply, DBUS_TYPE_STRING, &ic_introspection_xml, DBUS_TYPE_INVALID);
792     }
793 
794     /* ic is not NULL */
795     if (!reply && ic) {
796         DBusError error;
797         dbus_error_init(&error);
798         if (strcmp(dbus_message_get_sender(msg), GetIPCIC(ic)->sender) != 0) {
799             reply = dbus_message_new_error(msg, "org.fcitx.Fcitx.Error", "Invalid sender");
800         } else if (dbus_message_is_method_call(msg, FCITX_IC_DBUS_INTERFACE, "EnableIC")) {
801             FcitxInstanceEnableIM(ipc->owner, ic, false);
802             reply = dbus_message_new_method_return(msg);
803         } else if (dbus_message_is_method_call(msg, FCITX_IC_DBUS_INTERFACE, "CloseIC")) {
804             FcitxInstanceCloseIM(ipc->owner, ic);
805             reply = dbus_message_new_method_return(msg);
806         } else if (dbus_message_is_method_call(msg, FCITX_IC_DBUS_INTERFACE, "FocusIn")) {
807             IPCICFocusIn(ipc, ic);
808             reply = dbus_message_new_method_return(msg);
809         } else if (dbus_message_is_method_call(msg, FCITX_IC_DBUS_INTERFACE, "FocusOut")) {
810             IPCICFocusOut(ipc, ic);
811             reply = dbus_message_new_method_return(msg);
812         } else if (dbus_message_is_method_call(msg, FCITX_IC_DBUS_INTERFACE, "Reset")) {
813             IPCICReset(ipc, ic);
814             reply = dbus_message_new_method_return(msg);
815         } else if (dbus_message_is_method_call(msg, FCITX_IC_DBUS_INTERFACE, "MouseEvent")) {
816             reply = dbus_message_new_method_return(msg);
817         } else if (dbus_message_is_method_call(msg, FCITX_IC_DBUS_INTERFACE, "SetCursorLocation")) {
818             int x, y;
819             if (dbus_message_get_args(msg, &error, DBUS_TYPE_INT32, &x, DBUS_TYPE_INT32, &y, DBUS_TYPE_INVALID)) {
820                 IPCICSetCursorRect(ipc, ic, x, y, 0, 0);
821                 reply = dbus_message_new_method_return(msg);
822             } else {
823                 reply = FcitxDBusPropertyUnknownMethod(msg);
824             }
825         } else if (dbus_message_is_method_call(msg, FCITX_IC_DBUS_INTERFACE, "SetCursorRect")) {
826             int x, y, w, h;
827             if (dbus_message_get_args(msg, &error,
828                 DBUS_TYPE_INT32, &x, DBUS_TYPE_INT32, &y,
829                 DBUS_TYPE_INT32, &w, DBUS_TYPE_INT32, &h,
830                 DBUS_TYPE_INVALID)) {
831                 IPCICSetCursorRect(ipc, ic, x, y, w, h);
832                 reply = dbus_message_new_method_return(msg);
833             } else {
834                 reply = FcitxDBusPropertyUnknownMethod(msg);
835             }
836         } else if (dbus_message_is_method_call(msg, FCITX_IC_DBUS_INTERFACE, "SetCapacity")) {
837             uint32_t flags;
838             if (dbus_message_get_args(msg, &error, DBUS_TYPE_UINT32, &flags, DBUS_TYPE_INVALID)) {
839                 ic->contextCaps = flags;
840                 if (!(ic->contextCaps & CAPACITY_SURROUNDING_TEXT)) {
841                     if (GetIPCIC(ic)->surroundingText)
842                         free(GetIPCIC(ic)->surroundingText);
843                     GetIPCIC(ic)->surroundingText = NULL;
844                 }
845                 if (ic->contextCaps & CAPACITY_GET_IM_INFO_ON_FOCUS) {
846                     IPCUpdateIMInfoForIC(ipc);
847                 }
848                 reply = dbus_message_new_method_return(msg);
849             } else {
850                 reply = FcitxDBusPropertyUnknownMethod(msg);
851             }
852             result = DBUS_HANDLER_RESULT_HANDLED;
853         } else if (dbus_message_is_method_call(msg, FCITX_IC_DBUS_INTERFACE, "SetSurroundingText")) {
854             char* text;
855             uint32_t cursor, anchor;
856             if (dbus_message_get_args(msg, &error, DBUS_TYPE_STRING, &text,  DBUS_TYPE_UINT32, &cursor, DBUS_TYPE_UINT32, &anchor, DBUS_TYPE_INVALID)) {
857                 FcitxIPCIC* ipcic = GetIPCIC(ic);
858                 if (!ipcic->surroundingText || strcmp(ipcic->surroundingText, text) != 0 || cursor != ipcic->cursor || anchor != ipcic->anchor)
859                 {
860                     fcitx_utils_free(ipcic->surroundingText);
861                     ipcic->surroundingText = strdup(text);
862                     ipcic->cursor = cursor;
863                     ipcic->anchor = anchor;
864                     FcitxInstanceNotifyUpdateSurroundingText(ipc->owner, ic);
865                 }
866                 reply = dbus_message_new_method_return(msg);
867             } else {
868                 reply = FcitxDBusPropertyUnknownMethod(msg);
869             }
870             result = DBUS_HANDLER_RESULT_HANDLED;
871         } else if (dbus_message_is_method_call(msg, FCITX_IC_DBUS_INTERFACE, "SetSurroundingTextPosition")) {
872             uint32_t cursor, anchor;
873             if (dbus_message_get_args(msg, &error,  DBUS_TYPE_UINT32, &cursor, DBUS_TYPE_UINT32, &anchor, DBUS_TYPE_INVALID)) {
874                 FcitxIPCIC* ipcic = GetIPCIC(ic);
875                 if (cursor != ipcic->cursor || anchor != ipcic->anchor)
876                 {
877                     ipcic->cursor = cursor;
878                     ipcic->anchor = anchor;
879                     FcitxInstanceNotifyUpdateSurroundingText(ipc->owner, ic);
880                 }
881                 reply = dbus_message_new_method_return(msg);
882             } else {
883                 reply = FcitxDBusPropertyUnknownMethod(msg);
884             }
885             result = DBUS_HANDLER_RESULT_HANDLED;
886         } else if (dbus_message_is_method_call(msg, FCITX_IC_DBUS_INTERFACE, "DestroyIC")) {
887             FcitxInstanceDestroyIC(ipc->owner, ipc->frontendid, &id);
888             reply = dbus_message_new_method_return(msg);
889             result = DBUS_HANDLER_RESULT_HANDLED;
890         } else if (dbus_message_is_method_call(msg, FCITX_IC_DBUS_INTERFACE, "ProcessKeyEvent")) {
891             uint32_t keyval, keycode, state, t;
892             int ret, itype;
893             FcitxKeyEventType type;
894             if (dbus_message_get_args(msg, &error,
895                                       DBUS_TYPE_UINT32, &keyval,
896                                       DBUS_TYPE_UINT32, &keycode,
897                                       DBUS_TYPE_UINT32, &state,
898                                       DBUS_TYPE_INT32, &itype,
899                                       DBUS_TYPE_UINT32, &t,
900                                       DBUS_TYPE_INVALID)) {
901                 type = itype;
902                 ret = IPCProcessKey(ipc, ic, keyval, keycode, state, t, type);
903 
904                 reply = dbus_message_new_method_return(msg);
905 
906                 dbus_message_append_args(reply, DBUS_TYPE_INT32, &ret, DBUS_TYPE_INVALID);
907                 flush = true;
908             } else {
909                 reply = FcitxDBusPropertyUnknownMethod(msg);
910             }
911         }
912         dbus_error_free(&error);
913     }
914 
915     if (reply) {
916         dbus_connection_send(connection, reply, NULL);
917         dbus_message_unref(reply);
918         if (flush) {
919             dbus_connection_flush(connection);
920         }
921         result = DBUS_HANDLER_RESULT_HANDLED;
922     }
923 
924     return result;
925 }
926 
IPCProcessKey(FcitxIPCFrontend * ipc,FcitxInputContext * callic,const uint32_t originsym,const uint32_t keycode,const uint32_t originstate,uint32_t t,FcitxKeyEventType type)927 static int IPCProcessKey(FcitxIPCFrontend* ipc, FcitxInputContext* callic, const uint32_t originsym, const uint32_t keycode, const uint32_t originstate, uint32_t t, FcitxKeyEventType type)
928 {
929     FcitxInputContext* ic = FcitxInstanceGetCurrentIC(ipc->owner);
930     FcitxGlobalConfig* config = FcitxInstanceGetGlobalConfig(ipc->owner);
931     FcitxInputState* input = FcitxInstanceGetInputState(ipc->owner);
932 
933     if (ic == NULL || ic->frontendid != callic->frontendid || GetIPCIC(ic)->id != GetIPCIC(callic)->id) {
934         FcitxInstanceSetCurrentIC(ipc->owner, callic);
935         FcitxUIOnInputFocus(ipc->owner);
936     }
937     ic = callic;
938     FcitxKeySym sym;
939     unsigned int state;
940 
941     state = originstate & FcitxKeyState_SimpleMask;
942     state &= FcitxKeyState_UsedMask;
943     FcitxHotkeyGetKey(originsym, state, &sym, &state);
944     FcitxLog(DEBUG,
945              "KeyRelease=%d  state=%d  KEYCODE=%d  KEYSYM=%u ",
946              (type == FCITX_RELEASE_KEY), state, keycode, sym);
947 
948     if (originsym == 0)
949         return 0;
950 
951     if (ic->state == IS_CLOSED && type == FCITX_PRESS_KEY && FcitxHotkeyIsHotKey(sym, state, config->hkTrigger)) {
952         FcitxInstanceEnableIM(ipc->owner, ic, false);
953         return 1;
954     }
955     else if (ic->state == IS_CLOSED) {
956         return 0;
957     }
958 
959     FcitxInputStateSetKeyCode(input, keycode);
960     FcitxInputStateSetKeySym(input, originsym);
961     FcitxInputStateSetKeyState(input, originstate);
962     INPUT_RETURN_VALUE retVal = FcitxInstanceProcessKey(ipc->owner, type,
963                                            t,
964                                            sym, state);
965     FcitxInputStateSetKeyCode(input, 0);
966     FcitxInputStateSetKeySym(input, 0);
967     FcitxInputStateSetKeyState(input, 0);
968 
969     if (retVal & IRV_FLAG_FORWARD_KEY || retVal == IRV_TO_PROCESS)
970         return 0;
971     else
972         return 1;
973 }
974 
IPCICFocusIn(FcitxIPCFrontend * ipc,FcitxInputContext * ic)975 static void IPCICFocusIn(FcitxIPCFrontend* ipc, FcitxInputContext* ic)
976 {
977     if (ic == NULL)
978         return;
979 
980     FcitxInputContext* oldic = FcitxInstanceGetCurrentIC(ipc->owner);
981 
982     if (oldic && oldic != ic)
983         FcitxUICommitPreedit(ipc->owner);
984 
985     if (!FcitxInstanceSetCurrentIC(ipc->owner, ic))
986         return;
987 
988 
989     if (ic) {
990         FcitxUIOnInputFocus(ipc->owner);
991     } else {
992         FcitxUICloseInputWindow(ipc->owner);
993         FcitxUIMoveInputWindow(ipc->owner);
994     }
995 
996     return;
997 }
998 
IPCICFocusOut(FcitxIPCFrontend * ipc,FcitxInputContext * ic)999 static void IPCICFocusOut(FcitxIPCFrontend* ipc, FcitxInputContext* ic)
1000 {
1001     FcitxInputContext* currentic = FcitxInstanceGetCurrentIC(ipc->owner);
1002     if (ic && ic == currentic) {
1003         FcitxUICommitPreedit(ipc->owner);
1004         FcitxUICloseInputWindow(ipc->owner);
1005         FcitxInstanceSetCurrentIC(ipc->owner, NULL);
1006         FcitxUIOnInputUnFocus(ipc->owner);
1007     }
1008 
1009     return;
1010 }
1011 
IPCICReset(FcitxIPCFrontend * ipc,FcitxInputContext * ic)1012 static void IPCICReset(FcitxIPCFrontend* ipc, FcitxInputContext* ic)
1013 {
1014     FcitxInputContext* currentic = FcitxInstanceGetCurrentIC(ipc->owner);
1015     if (ic && ic == currentic) {
1016         FcitxUICloseInputWindow(ipc->owner);
1017         FcitxInstanceResetInput(ipc->owner);
1018     }
1019 
1020     return;
1021 }
1022 
IPCICSetCursorRect(FcitxIPCFrontend * ipc,FcitxInputContext * ic,int x,int y,int w,int h)1023 static void IPCICSetCursorRect(FcitxIPCFrontend* ipc, FcitxInputContext* ic, int x, int y, int w, int h)
1024 {
1025     ic->offset_x = x;
1026     ic->offset_y = y;
1027     GetIPCIC(ic)->width = w;
1028     GetIPCIC(ic)->height = h;
1029     FcitxUIMoveInputWindow(ipc->owner);
1030 
1031     return;
1032 }
1033 
IPCUpdatePreedit(void * arg,FcitxInputContext * ic)1034 void IPCUpdatePreedit(void* arg, FcitxInputContext* ic)
1035 {
1036     FcitxIPCFrontend* ipc = (FcitxIPCFrontend*) arg;
1037     FcitxInputState* input = FcitxInstanceGetInputState(ipc->owner);
1038     FcitxMessages* clientPreedit = FcitxInputStateGetClientPreedit(input);
1039     int i = 0;
1040     for (i = 0; i < FcitxMessagesGetMessageCount(clientPreedit) ; i ++) {
1041         char* str = FcitxMessagesGetMessageString(clientPreedit, i);
1042         if (!fcitx_utf8_check_string(str))
1043             return;
1044     }
1045 
1046     /* a small optimization, don't need to update empty preedit */
1047     FcitxIPCIC* ipcic = GetIPCIC(ic);
1048     if (ipcic->lastPreeditIsEmpty && FcitxMessagesGetMessageCount(clientPreedit) == 0)
1049         return;
1050 
1051     ipcic->lastPreeditIsEmpty = (FcitxMessagesGetMessageCount(clientPreedit) == 0);
1052 
1053     if (ic->contextCaps & CAPACITY_FORMATTED_PREEDIT) {
1054         DBusMessage* msg = dbus_message_new_signal(GetIPCIC(ic)->path, // object name of the signal
1055                         FCITX_IC_DBUS_INTERFACE, // interface name of the signal
1056                         "UpdateFormattedPreedit"); // name of the signal
1057 
1058         DBusMessageIter args, array, sub;
1059         dbus_message_iter_init_append(msg, &args);
1060         dbus_message_iter_open_container(&args, DBUS_TYPE_ARRAY, "(si)", &array);
1061         int i = 0;
1062         for (i = 0; i < FcitxMessagesGetMessageCount(clientPreedit) ; i ++) {
1063             dbus_message_iter_open_container(&array, DBUS_TYPE_STRUCT, 0, &sub);
1064             char* str = FcitxMessagesGetMessageString(clientPreedit, i);
1065             char* needtofree = FcitxInstanceProcessOutputFilter(ipc->owner, str);
1066             if (needtofree) {
1067                 str = needtofree;
1068             }
1069             int type = FcitxMessagesGetClientMessageType(clientPreedit, i);
1070             dbus_message_iter_append_basic(&sub, DBUS_TYPE_STRING, &str);
1071             dbus_message_iter_append_basic(&sub, DBUS_TYPE_INT32, &type);
1072             dbus_message_iter_close_container(&array, &sub);
1073             if (needtofree)
1074                 free(needtofree);
1075         }
1076         dbus_message_iter_close_container(&args, &array);
1077 
1078         int iCursorPos = FcitxInputStateGetClientCursorPos(input);
1079         dbus_message_iter_append_basic(&args, DBUS_TYPE_INT32, &iCursorPos);
1080 
1081         IPCSendSignal(ipc, GetIPCIC(ic), msg);
1082     }
1083     else {
1084         FcitxIPCFrontend* ipc = (FcitxIPCFrontend*) arg;
1085         FcitxInputState* input = FcitxInstanceGetInputState(ipc->owner);
1086         DBusMessage* msg = dbus_message_new_signal(GetIPCIC(ic)->path, // object name of the signal
1087                         FCITX_IC_DBUS_INTERFACE, // interface name of the signal
1088                         "UpdatePreedit"); // name of the signal
1089 
1090         char* strPreedit = FcitxUIMessagesToCString(FcitxInputStateGetClientPreedit(input));
1091         char* str = FcitxInstanceProcessOutputFilter(ipc->owner, strPreedit);
1092         if (str) {
1093             free(strPreedit);
1094             strPreedit = str;
1095         }
1096 
1097         int iCursorPos = FcitxInputStateGetClientCursorPos(input);
1098 
1099         dbus_message_append_args(msg, DBUS_TYPE_STRING, &strPreedit, DBUS_TYPE_INT32, &iCursorPos, DBUS_TYPE_INVALID);
1100 
1101         IPCSendSignal(ipc, GetIPCIC(ic), msg);
1102         free(strPreedit);
1103     }
1104 }
1105 
IPCDeleteSurroundingText(void * arg,FcitxInputContext * ic,int offset,unsigned int size)1106 void IPCDeleteSurroundingText(void* arg, FcitxInputContext* ic, int offset, unsigned int size)
1107 {
1108     FcitxIPCFrontend* ipc = (FcitxIPCFrontend*) arg;
1109     FcitxIPCIC* ipcic = GetIPCIC(ic);
1110 
1111     /*
1112      * do the real deletion here, and client might update it, but input method itself
1113      * would expect a up to date value after this call.
1114      *
1115      * Make their life easier.
1116      */
1117     if (ipcic->surroundingText) {
1118         int cursor_pos = ipcic->cursor + offset;
1119         size_t len = fcitx_utf8_strlen (ipcic->surroundingText);
1120         if (cursor_pos >= 0 && len - cursor_pos >= size) {
1121             /*
1122              * the original size must be larger, so we can do in-place copy here
1123              * without alloc new string
1124              */
1125             char* start = fcitx_utf8_get_nth_char(ipcic->surroundingText, cursor_pos);
1126             char* end = fcitx_utf8_get_nth_char(start, size);
1127 
1128             int copylen = strlen(end);
1129 
1130             memmove (start, end, sizeof(char) * copylen);
1131             start[copylen] = 0;
1132             ipcic->cursor = cursor_pos;
1133         } else {
1134             ipcic->surroundingText[0] = '\0';
1135             ipcic->cursor = 0;
1136         }
1137         ipcic->anchor = ipcic->cursor;
1138     }
1139 
1140 
1141     DBusMessage* msg = dbus_message_new_signal(GetIPCIC(ic)->path, // object name of the signal
1142                        FCITX_IC_DBUS_INTERFACE, // interface name of the signal
1143                        "DeleteSurroundingText"); // name of the signal
1144 
1145     dbus_message_append_args(msg, DBUS_TYPE_INT32, &offset, DBUS_TYPE_UINT32, &size, DBUS_TYPE_INVALID);
1146 
1147     IPCSendSignal(ipc, GetIPCIC(ic), msg);
1148 }
1149 
1150 
IPCGetSurroundingText(void * arg,FcitxInputContext * ic,char ** str,unsigned int * cursor,unsigned int * anchor)1151 boolean IPCGetSurroundingText(void* arg, FcitxInputContext* ic, char** str, unsigned int *cursor, unsigned int *anchor)
1152 {
1153     FCITX_UNUSED(arg);
1154     FcitxIPCIC* ipcic = GetIPCIC(ic);
1155 
1156     if (!ipcic->surroundingText)
1157         return false;
1158 
1159     if (str)
1160         *str = strdup(ipcic->surroundingText);
1161 
1162     if (cursor)
1163         *cursor = ipcic->cursor;
1164 
1165     if (anchor)
1166         *anchor = ipcic->anchor;
1167 
1168     return true;
1169 }
1170 
1171 
IPCUpdateClientSideUI(void * arg,FcitxInputContext * ic)1172 void IPCUpdateClientSideUI(void* arg, FcitxInputContext* ic)
1173 {
1174     FcitxIPCFrontend* ipc = (FcitxIPCFrontend*) arg;
1175     FcitxInputState* input = FcitxInstanceGetInputState(ipc->owner);
1176     DBusMessage* msg = dbus_message_new_signal(GetIPCIC(ic)->path, // object name of the signal
1177                        FCITX_IC_DBUS_INTERFACE, // interface name of the signal
1178                        "UpdateClientSideUI"); // name of the signal
1179 
1180     char *str;
1181     char* strAuxUp = FcitxUIMessagesToCString(FcitxInputStateGetAuxUp(input));
1182     str = FcitxInstanceProcessOutputFilter(ipc->owner, strAuxUp);
1183     if (str) {
1184         free(strAuxUp);
1185         strAuxUp = str;
1186     }
1187     char* strAuxDown = FcitxUIMessagesToCString(FcitxInputStateGetAuxDown(input));
1188     str = FcitxInstanceProcessOutputFilter(ipc->owner, strAuxDown);
1189     if (str) {
1190         free(strAuxDown);
1191         strAuxDown = str;
1192     }
1193     char* strPreedit = FcitxUIMessagesToCString(FcitxInputStateGetPreedit(input));
1194     str = FcitxInstanceProcessOutputFilter(ipc->owner, strPreedit);
1195     if (str) {
1196         free(strPreedit);
1197         strPreedit = str;
1198     }
1199     char* candidateword = FcitxUICandidateWordToCString(ipc->owner);
1200     str = FcitxInstanceProcessOutputFilter(ipc->owner, candidateword);
1201     if (str) {
1202         free(candidateword);
1203         candidateword = str;
1204     }
1205     FcitxIM* im = FcitxInstanceGetCurrentIM(ipc->owner);
1206     char* imname = NULL;
1207     if (im == NULL)
1208         imname = "En";
1209     else
1210         imname = im->strName;
1211 
1212     int iCursorPos = FcitxInputStateGetCursorPos(input);
1213 
1214     dbus_message_append_args(msg,
1215                              DBUS_TYPE_STRING, &strAuxUp,
1216                              DBUS_TYPE_STRING, &strAuxDown,
1217                              DBUS_TYPE_STRING, &strPreedit,
1218                              DBUS_TYPE_STRING, &candidateword,
1219                              DBUS_TYPE_STRING, &imname,
1220                              DBUS_TYPE_INT32, &iCursorPos,
1221                              DBUS_TYPE_INVALID);
1222 
1223     IPCSendSignal(ipc, GetIPCIC(ic), msg);
1224     free(strAuxUp);
1225     free(strAuxDown);
1226     free(strPreedit);
1227     free(candidateword);
1228 }
1229 
IPCCheckICFromSameApplication(void * arg,FcitxInputContext * icToCheck,FcitxInputContext * ic)1230 boolean IPCCheckICFromSameApplication(void* arg, FcitxInputContext* icToCheck, FcitxInputContext* ic)
1231 {
1232     FCITX_UNUSED(arg);
1233     FcitxInputContext2* ic2ToCheck = (FcitxInputContext2*)icToCheck;
1234     FcitxInputContext2* ic2 = (FcitxInputContext2*)ic;
1235     if (ic2->prgname == NULL || ic2ToCheck->prgname == NULL)
1236         return false;
1237     return strcmp(ic2ToCheck->prgname, ic2->prgname) == 0;
1238 }
1239 
IPCEmitPropertiesChanged(void * arg,const char * const * properties)1240 void IPCEmitPropertiesChanged(void* arg, const char* const* properties)
1241 {
1242     if (!properties || !*properties)
1243         return;
1244 
1245     FcitxIPCFrontend* ipc = (FcitxIPCFrontend*) arg;
1246     DBusMessage* msg = dbus_message_new_signal(FCITX_IM_DBUS_PATH, // object name of the signal
1247                        DBUS_INTERFACE_PROPERTIES, // interface name of the signal
1248                        "PropertiesChanged"); // name of the signal
1249 
1250     DBusMessageIter args;
1251     DBusMessageIter changed_properties, invalidated_properties;
1252     char sinterface[] = FCITX_IM_DBUS_INTERFACE;
1253     char* interface = sinterface;
1254     dbus_message_iter_init_append(msg, &args);
1255     dbus_message_iter_append_basic(&args, DBUS_TYPE_STRING, &interface);
1256 
1257     dbus_message_iter_open_container(&args, DBUS_TYPE_ARRAY, "{sv}", &changed_properties);
1258     dbus_message_iter_close_container(&args, &changed_properties);
1259 
1260     dbus_message_iter_open_container(&args, DBUS_TYPE_ARRAY, "s", &invalidated_properties);
1261     for (; *properties; properties++)
1262         dbus_message_iter_append_basic(&invalidated_properties, DBUS_TYPE_STRING, properties);
1263     dbus_message_iter_close_container(&args, &invalidated_properties);
1264 
1265     IPCSendSignal(ipc, NULL, msg);
1266 }
1267 
IPCEmitPropertyChanged(void * arg,const char * property)1268 void IPCEmitPropertyChanged(void* arg, const char* property)
1269 {
1270     const char* properties[] = { property, NULL };
1271     IPCEmitPropertiesChanged(arg, properties);
1272 }
1273 
IPCGetPropertyIMList(void * arg,DBusMessageIter * args)1274 void IPCGetPropertyIMList(void* arg, DBusMessageIter* args)
1275 {
1276     FcitxIPCFrontend* ipc = (FcitxIPCFrontend*) arg;
1277     FcitxInstance* instance = ipc->owner;
1278     DBusMessageIter sub, ssub;
1279     dbus_message_iter_open_container(args, DBUS_TYPE_ARRAY, "(sssb)", &sub);
1280     FcitxIM* ime;
1281     UT_array* imes = FcitxInstanceGetIMEs(instance);
1282     for (ime = (FcitxIM*) utarray_front(imes);
1283             ime != NULL;
1284             ime = (FcitxIM*) utarray_next(imes, ime)) {
1285         dbus_message_iter_open_container(&sub, DBUS_TYPE_STRUCT, 0, &ssub);
1286         boolean enable = true;
1287         char* name = ime->strName;
1288         char* uniqueName = ime->uniqueName;
1289         char* langCode = ime->langCode;
1290         dbus_message_iter_append_basic(&ssub, DBUS_TYPE_STRING, &name);
1291         dbus_message_iter_append_basic(&ssub, DBUS_TYPE_STRING, &uniqueName);
1292         dbus_message_iter_append_basic(&ssub, DBUS_TYPE_STRING, &langCode);
1293         dbus_message_iter_append_basic(&ssub, DBUS_TYPE_BOOLEAN, &enable);
1294         dbus_message_iter_close_container(&sub, &ssub);
1295     }
1296 
1297     UT_array* availimes = FcitxInstanceGetAvailIMEs(instance);
1298     for (ime = (FcitxIM*) utarray_front(availimes);
1299             ime != NULL;
1300             ime = (FcitxIM*) utarray_next(availimes, ime)) {
1301         if (!FcitxInstanceGetIMFromIMList(instance, IMAS_Enable, ime->uniqueName)) {
1302             dbus_message_iter_open_container(&sub, DBUS_TYPE_STRUCT, 0, &ssub);
1303             boolean enable = false;
1304             char* name = ime->strName;
1305             char* uniqueName = ime->uniqueName;
1306             char* langCode = ime->langCode;
1307             dbus_message_iter_append_basic(&ssub, DBUS_TYPE_STRING, &name);
1308             dbus_message_iter_append_basic(&ssub, DBUS_TYPE_STRING, &uniqueName);
1309             dbus_message_iter_append_basic(&ssub, DBUS_TYPE_STRING, &langCode);
1310             dbus_message_iter_append_basic(&ssub, DBUS_TYPE_BOOLEAN, &enable);
1311             dbus_message_iter_close_container(&sub, &ssub);
1312         }
1313     }
1314     dbus_message_iter_close_container(args, &sub);
1315 }
1316 
IPCSetPropertyIMList(void * arg,DBusMessageIter * args)1317 void IPCSetPropertyIMList(void* arg, DBusMessageIter* args)
1318 {
1319     FcitxIPCFrontend* ipc = (FcitxIPCFrontend*) arg;
1320     FcitxInstance* instance = ipc->owner;
1321     DBusMessageIter sub, ssub;
1322 
1323     if (dbus_message_iter_get_arg_type(args) != DBUS_TYPE_ARRAY)
1324         return;
1325 
1326     dbus_message_iter_recurse(args, &sub);
1327 
1328     char* result = NULL;
1329     while (dbus_message_iter_get_arg_type(&sub) == DBUS_TYPE_STRUCT) {
1330         dbus_message_iter_recurse(&sub, &ssub);
1331         char* name, *uniqueName, *langCode;
1332         boolean enable;
1333         if (dbus_message_iter_get_arg_type(&ssub) != DBUS_TYPE_STRING)
1334             goto ipc_set_imlist_end;
1335         dbus_message_iter_get_basic(&ssub, &name);
1336         dbus_message_iter_next(&ssub);
1337 
1338         if (dbus_message_iter_get_arg_type(&ssub) != DBUS_TYPE_STRING)
1339             goto ipc_set_imlist_end;
1340         dbus_message_iter_get_basic(&ssub, &uniqueName);
1341         dbus_message_iter_next(&ssub);
1342 
1343         if (dbus_message_iter_get_arg_type(&ssub) != DBUS_TYPE_STRING)
1344             goto ipc_set_imlist_end;
1345         dbus_message_iter_get_basic(&ssub, &langCode);
1346         dbus_message_iter_next(&ssub);
1347 
1348         if (dbus_message_iter_get_arg_type(&ssub) != DBUS_TYPE_BOOLEAN)
1349             goto ipc_set_imlist_end;
1350         dbus_message_iter_get_basic(&ssub, &enable);
1351         dbus_message_iter_next(&ssub);
1352 
1353         char* newresult;
1354         if (!result) {
1355             fcitx_utils_alloc_cat_str(newresult, uniqueName, ":",
1356                                       enable ? "True" : "False");
1357         } else {
1358             fcitx_utils_alloc_cat_str(newresult, result, ",", uniqueName, ":",
1359                                       enable ? "True" : "False");
1360         }
1361         if (result)
1362             free(result);
1363         result = newresult;
1364     ipc_set_imlist_end:
1365         dbus_message_iter_next(&sub);
1366     }
1367 
1368     FcitxLog(DEBUG, "%s", result);
1369     if (result) {
1370         FcitxProfile* profile = FcitxInstanceGetProfile(instance);
1371         if (profile->imList)
1372             free(profile->imList);
1373         profile->imList = result;
1374         FcitxInstanceUpdateIMList(instance);
1375     }
1376 }
1377 
IPCUpdateIMList(void * arg)1378 void IPCUpdateIMList(void* arg)
1379 {
1380     IPCEmitPropertyChanged(arg, "IMList");
1381 }
1382 
IPCGetPropertyCurrentIM(void * arg,DBusMessageIter * args)1383 void IPCGetPropertyCurrentIM(void* arg, DBusMessageIter* args)
1384 {
1385     FcitxIPCFrontend* ipc = (FcitxIPCFrontend*) arg;
1386     FcitxInstance* instance = ipc->owner;
1387     FcitxIM* im = FcitxInstanceGetCurrentIM(instance);
1388     const char* currentIM = im && im->uniqueName ? im->uniqueName : "";
1389 
1390     dbus_message_iter_append_basic(args, DBUS_TYPE_STRING, &currentIM);
1391 }
1392 
IPCSetPropertyCurrentIM(void * arg,DBusMessageIter * args)1393 void IPCSetPropertyCurrentIM(void* arg, DBusMessageIter* args)
1394 {
1395     FcitxIPCFrontend* ipc = (FcitxIPCFrontend*) arg;
1396     FcitxInstance* instance = ipc->owner;
1397     const char* currentIM;
1398 
1399     if (dbus_message_iter_get_arg_type(args) != DBUS_TYPE_STRING)
1400         return;
1401 
1402     dbus_message_iter_get_basic(args, &currentIM);
1403     FcitxInstanceSwitchIMByName(instance, currentIM);
1404 }
1405 
IPCUpdateCurrentIM(void * arg)1406 void IPCUpdateCurrentIM(void* arg)
1407 {
1408     IPCEmitPropertyChanged(arg, "CurrentIM");
1409     IPCUpdateIMInfoForIC(arg);
1410 }
1411 
IPCUpdateIMInfoForIC(void * arg)1412 void IPCUpdateIMInfoForIC(void* arg)
1413 {
1414     FcitxIPCFrontend* ipc = (FcitxIPCFrontend*) arg;
1415     FcitxInputContext* ic = FcitxInstanceGetCurrentIC(ipc->owner);
1416     if (ic && (ic->contextCaps & CAPACITY_GET_IM_INFO_ON_FOCUS) && ic->frontendid == ipc->frontendid) {
1417         FcitxIM* im = FcitxInstanceGetCurrentIM(ipc->owner);
1418         const char* name = (im && im->strName && fcitx_utf8_check_string(im->strName)) ? im->strName : "";
1419         const char* uniqueName = (im && im->uniqueName && fcitx_utf8_check_string(im->uniqueName)) ? im->uniqueName : "";
1420         const char* langCode = (im && fcitx_utf8_check_string(im->langCode)) ? im->langCode : "";
1421 
1422         if (fcitx_utils_strcmp0(GetIPCIC(ic)->lastSentIMInfo.name, name) == 0 &&
1423             fcitx_utils_strcmp0(GetIPCIC(ic)->lastSentIMInfo.uniqueName, uniqueName) == 0 &&
1424             fcitx_utils_strcmp0(GetIPCIC(ic)->lastSentIMInfo.langCode, langCode) == 0) {
1425             return;
1426         }
1427 
1428         DBusMessage* msg = dbus_message_new_signal(GetIPCIC(ic)->path, // object name of the signal
1429                         FCITX_IC_DBUS_INTERFACE, // interface name of the signal
1430                         "CurrentIM"); // name of the signal
1431 
1432         fcitx_utils_string_swap(&GetIPCIC(ic)->lastSentIMInfo.name, name);
1433         fcitx_utils_string_swap(&GetIPCIC(ic)->lastSentIMInfo.uniqueName, uniqueName);
1434         fcitx_utils_string_swap(&GetIPCIC(ic)->lastSentIMInfo.langCode, langCode);
1435 
1436         dbus_message_append_args(msg, DBUS_TYPE_STRING, &name, DBUS_TYPE_STRING, &uniqueName, DBUS_TYPE_STRING, &langCode, DBUS_TYPE_INVALID);
1437         IPCSendSignal(ipc, GetIPCIC(ic), msg);
1438     }
1439 }
1440 
IPCGetPid(void * arg,FcitxInputContext * ic)1441 pid_t IPCGetPid(void* arg, FcitxInputContext* ic)
1442 {
1443     FCITX_UNUSED(arg);
1444     return GetIPCIC(ic)->pid;
1445 }
1446 
1447 // kate: indent-mode cstyle; space-indent on; indent-width 0;
1448