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 <X11/Xutil.h>
22 
23 #include "fcitx/ui.h"
24 #include "fcitx/ime.h"
25 #include "fcitx/instance.h"
26 #include "fcitx/module.h"
27 #include "fcitx-config/hotkey.h"
28 #include "fcitx-utils/log.h"
29 #include "xim.h"
30 #include "ximhandler.h"
31 #include "Xi18n.h"
32 #include "IC.h"
33 #include "ximqueue.h"
34 
XIMOpenHandler(FcitxXimFrontend * xim,IMOpenStruct * call_data)35 Bool XIMOpenHandler(FcitxXimFrontend* xim, IMOpenStruct * call_data)
36 {
37     FCITX_UNUSED(xim);
38     FCITX_UNUSED(call_data);
39     return True;
40 }
41 
42 
XIMGetICValuesHandler(FcitxXimFrontend * xim,IMChangeICStruct * call_data)43 Bool XIMGetICValuesHandler(FcitxXimFrontend* xim, IMChangeICStruct * call_data)
44 {
45     XimGetIC(xim, call_data);
46 
47     return True;
48 }
49 
XIMSetICValuesHandler(FcitxXimFrontend * xim,IMChangeICStruct * call_data)50 Bool XIMSetICValuesHandler(FcitxXimFrontend* xim, IMChangeICStruct * call_data)
51 {
52     XimSetIC(xim, call_data);
53     FcitxInputContext* ic = FcitxInstanceFindIC(xim->owner, xim->frontendid, &call_data->icid);
54     SetTrackPos(xim, ic, call_data);
55 
56     return True;
57 }
58 
XIMSetFocusHandler(FcitxXimFrontend * xim,IMChangeFocusStruct * call_data)59 Bool XIMSetFocusHandler(FcitxXimFrontend* xim, IMChangeFocusStruct * call_data)
60 {
61     FcitxInputContext* ic =  FcitxInstanceFindIC(xim->owner, xim->frontendid, &call_data->icid);
62     if (ic == NULL)
63         return True;
64 
65     FcitxInputContext* oldic = FcitxInstanceGetCurrentIC(xim->owner);
66 
67     if (oldic && oldic != ic)
68         FcitxUICommitPreedit(xim->owner);
69 
70     if (!FcitxInstanceSetCurrentIC(xim->owner, ic))
71         return True;
72 
73     SetTrackPos(xim, ic, NULL);
74 
75     if (ic) {
76         FcitxUIOnInputFocus(xim->owner);
77     } else {
78         FcitxUICloseInputWindow(xim->owner);
79         FcitxUIMoveInputWindow(xim->owner);
80     }
81 
82     return True;
83 }
84 
XIMUnsetFocusHandler(FcitxXimFrontend * xim,IMChangeICStruct * call_data)85 Bool XIMUnsetFocusHandler(FcitxXimFrontend* xim, IMChangeICStruct * call_data)
86 {
87     FcitxInputContext* ic = FcitxInstanceGetCurrentIC(xim->owner);
88     if (ic && GetXimIC(ic)->id == call_data->icid) {
89         FcitxUICloseInputWindow(xim->owner);
90         FcitxInstanceResetInput(xim->owner);
91     }
92 
93     return True;
94 }
95 
XIMResetICHandler(FcitxXimFrontend * xim,IMResetICStruct * call_data)96 Bool XIMResetICHandler(FcitxXimFrontend* xim, IMResetICStruct * call_data)
97 {
98     FcitxInputContext* ic = FcitxInstanceGetCurrentIC(xim->owner);
99     if (ic && GetXimIC(ic)->id == call_data->icid) {
100         FcitxUICommitPreedit(xim->owner);
101         FcitxUICloseInputWindow(xim->owner);
102         FcitxInstanceSetCurrentIC(xim->owner, NULL);
103         FcitxUIOnInputUnFocus(xim->owner);
104     }
105 
106     return True;
107 }
108 
XIMCloseHandler(FcitxXimFrontend * xim,IMOpenStruct * call_data)109 Bool XIMCloseHandler(FcitxXimFrontend* xim, IMOpenStruct * call_data)
110 {
111     FCITX_UNUSED(call_data);
112     FcitxUICloseInputWindow(xim->owner);
113     FcitxInstanceSaveAllIM(xim->owner);
114     return True;
115 }
116 
XIMCreateICHandler(FcitxXimFrontend * xim,IMChangeICStruct * call_data)117 Bool XIMCreateICHandler(FcitxXimFrontend* xim, IMChangeICStruct * call_data)
118 {
119     FcitxInstanceCreateIC(xim->owner, xim->frontendid, call_data);
120     FcitxInputContext* ic = FcitxInstanceGetCurrentIC(xim->owner);
121 
122     if (ic == NULL) {
123         ic = FcitxInstanceFindIC(xim->owner, xim->frontendid, &call_data->icid);
124         if (FcitxInstanceSetCurrentIC(xim->owner, ic) && ic)
125             FcitxUIOnInputFocus(xim->owner);
126     }
127 
128     return True;
129 }
130 
XIMDestroyICHandler(FcitxXimFrontend * xim,IMChangeICStruct * call_data)131 Bool XIMDestroyICHandler(FcitxXimFrontend* xim, IMChangeICStruct * call_data)
132 {
133     FcitxInstanceDestroyIC(xim->owner, xim->frontendid, &call_data->icid);
134 
135     return True;
136 }
137 
SetTrackPos(FcitxXimFrontend * xim,FcitxInputContext * ic,IMChangeICStruct * call_data)138 void SetTrackPos(FcitxXimFrontend* xim, FcitxInputContext* ic, IMChangeICStruct * call_data)
139 {
140     if (ic == NULL)
141         return;
142 
143     int i;
144     FcitxXimIC* ximic = GetXimIC(ic);
145     if (call_data) {
146         XICAttribute *pre_attr = ((IMChangeICStruct *) call_data)->preedit_attr;
147 
148         for (i = 0; i < (int)((IMChangeICStruct *) call_data)->preedit_attr_num; i++, pre_attr++) {
149             if (!strcmp(XNSpotLocation, pre_attr->name)) {
150                 ximic->bHasCursorLocation = true;
151                 ximic->offset_x = (*(XPoint *) pre_attr->value).x;
152                 ximic->offset_y = (*(XPoint *) pre_attr->value).y;
153             }
154         }
155     }
156 
157     Window window;
158     if (!(window = ximic->focus_win))
159         window = ximic->client_win;
160 
161     if (window != None) {
162         Window dst;
163         XWindowAttributes attr;
164         XGetWindowAttributes(xim->display, window, &attr);
165 
166         if (ximic->offset_x < 0 && ximic->offset_y < 0) {
167 
168             XTranslateCoordinates(xim->display, window, RootWindow(xim->display, xim->iScreen),
169                                   0, attr.height,
170                                   &ic->offset_x, &ic->offset_y,
171                                   &dst
172                                  );
173         } else {
174             XTranslateCoordinates(xim->display, window, RootWindow(xim->display, xim->iScreen),
175                                   ximic->offset_x, ximic->offset_y,
176                                   &ic->offset_x, &ic->offset_y,
177                                   &dst);
178         }
179     }
180 
181     if (ic == FcitxInstanceGetCurrentIC(xim->owner))
182         FcitxUIMoveInputWindow(xim->owner);
183 }
184 
XIMProcessKey(FcitxXimFrontend * xim,IMForwardEventStruct * call_data)185 void XIMProcessKey(FcitxXimFrontend* xim, IMForwardEventStruct * call_data)
186 {
187     KeySym originsym;
188     FcitxKeySym sym;
189     XKeyEvent *kev;
190     int keyCount;
191     uint32_t state;
192     char strbuf[STRBUFLEN];
193     FcitxInputContext* ic = FcitxInstanceGetCurrentIC(xim->owner);
194     FcitxGlobalConfig* config = FcitxInstanceGetGlobalConfig(xim->owner);
195     FcitxInputState* input = FcitxInstanceGetInputState(xim->owner);
196 
197     if (ic == NULL) {
198         ic = FcitxInstanceFindIC(xim->owner, xim->frontendid, &call_data->icid);
199         if (FcitxInstanceSetCurrentIC(xim->owner, ic) && ic)
200             FcitxUIOnInputFocus(xim->owner);
201     }
202 
203     if (ic == NULL)
204         return;
205 
206     if (ic->frontendid != xim->frontendid || GetXimIC(ic)->id != call_data->icid) {
207         ic = FcitxInstanceFindIC(xim->owner, xim->frontendid, &call_data->icid);
208         if (ic == NULL)
209             return;
210         if (FcitxInstanceSetCurrentIC(xim->owner, ic))
211             FcitxUIOnInputFocus(xim->owner);
212     }
213 
214     kev = (XKeyEvent *) & call_data->event;
215     memset(strbuf, 0, STRBUFLEN);
216     keyCount = XLookupString(kev, strbuf, STRBUFLEN, &originsym, NULL);
217 
218     const uint32_t originstate = kev->state;
219     state = kev->state - (kev->state & FcitxKeyState_NumLock) - (kev->state & FcitxKeyState_CapsLock) - (kev->state & FcitxKeyState_ScrollLock);
220     state &= FcitxKeyState_UsedMask;
221     FcitxHotkeyGetKey((FcitxKeySym) originsym, state, &sym, &state);
222     FcitxLog(DEBUG,
223              "KeyRelease=%d  state=%d  KEYCODE=%d  KEYSYM=%d  keyCount=%d",
224              (call_data->event.type == KeyRelease), state, kev->keycode, (int) sym, keyCount);
225 
226     xim->currentSerialNumberCallData = call_data->serial_number;
227     xim->currentSerialNumberKey = kev->serial;
228 
229     FcitxKeyEventType type = (call_data->event.type == KeyRelease) ? (FCITX_RELEASE_KEY) : (FCITX_PRESS_KEY);
230 
231     if (ic->state == IS_CLOSED) {
232         if (type == FCITX_PRESS_KEY && FcitxHotkeyIsHotKey(sym, state, config->hkTrigger)) {
233             FcitxInstanceEnableIM(xim->owner, ic, false);
234             return;
235         } else {
236             XimForwardKeyInternal(xim,
237                                   GetXimIC(ic),
238                                   &call_data->event
239                                  );
240             return;
241         }
242     }
243 
244     FcitxInputStateSetKeyCode(input, kev->keycode);
245     FcitxInputStateSetKeySym(input, originsym);
246     FcitxInputStateSetKeyState(input, originstate);
247     INPUT_RETURN_VALUE retVal = FcitxInstanceProcessKey(xim->owner, type,
248                                            kev->time,
249                                            sym, state);
250     FcitxInputStateSetKeyCode(input, 0);
251     FcitxInputStateSetKeySym(input, 0);
252     FcitxInputStateSetKeyState(input, 0);
253 
254     if ((retVal & IRV_FLAG_FORWARD_KEY) || retVal == IRV_TO_PROCESS) {
255         XimForwardKeyInternal(xim,
256                               GetXimIC(ic),
257                               &call_data->event
258                              );
259     } else {
260         if (!GetXimIC(ic)->bHasCursorLocation)
261             SetTrackPos(xim, ic, NULL);
262     }
263     xim->currentSerialNumberCallData = xim->currentSerialNumberKey = 0L;
264 }
265 
266 
XimForwardKeyInternal(FcitxXimFrontend * xim,FcitxXimIC * ic,XEvent * xEvent)267 void XimForwardKeyInternal(FcitxXimFrontend *xim,
268                            FcitxXimIC* ic,
269                            XEvent* xEvent
270                           )
271 {
272     IMForwardEventStruct* forwardEvent = fcitx_utils_new(IMForwardEventStruct);
273 
274     forwardEvent->connect_id = ic->connect_id;
275     forwardEvent->icid = ic->id;
276     forwardEvent->major_code = XIM_FORWARD_EVENT;
277     forwardEvent->sync_bit = 0;
278     forwardEvent->serial_number = xim->currentSerialNumberCallData;
279 
280     memcpy(&(forwardEvent->event), xEvent, sizeof(XEvent));
281     XimPendingCall(xim, XCT_FORWARD, (XPointer)forwardEvent);
282 }
283 
284 void
XimPreeditCallbackStart(FcitxXimFrontend * xim,const FcitxXimIC * ic)285 XimPreeditCallbackStart(FcitxXimFrontend *xim, const FcitxXimIC* ic)
286 {
287     IMPreeditCBStruct* pcb = fcitx_utils_new(IMPreeditCBStruct);
288 
289     pcb->major_code = XIM_PREEDIT_START;
290     pcb->minor_code = 0;
291     pcb->connect_id = ic->connect_id;
292     pcb->icid = ic->id;
293     pcb->todo.return_value = 0;
294     XimPendingCall(xim, XCT_CALLCALLBACK, (XPointer) pcb);
295 }
296 
297 
298 void
XimPreeditCallbackDone(FcitxXimFrontend * xim,const FcitxXimIC * ic)299 XimPreeditCallbackDone(FcitxXimFrontend *xim, const FcitxXimIC* ic)
300 {
301     IMPreeditCBStruct* pcb = fcitx_utils_new(IMPreeditCBStruct);
302 
303     pcb->major_code = XIM_PREEDIT_DONE;
304     pcb->minor_code = 0;
305     pcb->connect_id = ic->connect_id;
306     pcb->icid = ic->id;
307     pcb->todo.return_value = 0;
308     XimPendingCall(xim, XCT_CALLCALLBACK, (XPointer) pcb);
309 }
310 
311 void
XimPreeditCallbackDraw(FcitxXimFrontend * xim,FcitxXimIC * ic,const char * preedit_string,int cursorPos)312 XimPreeditCallbackDraw(FcitxXimFrontend* xim, FcitxXimIC* ic,
313                        const char* preedit_string, int cursorPos)
314 {
315     XTextProperty tp;
316 
317     int i, len;
318 
319     if (preedit_string == NULL)
320         return;
321 
322     len = fcitx_utf8_strlen(preedit_string);
323 
324     if (len + 1 > xim->feedback_len) {
325         xim->feedback_len = len + 1;
326         xim->feedback = realloc(xim->feedback,
327                                 sizeof(XIMFeedback) * xim->feedback_len);
328     }
329 
330     FcitxInputState* input = FcitxInstanceGetInputState(xim->owner);
331     FcitxMessages* clientPreedit = FcitxInputStateGetClientPreedit(input);
332     int offset = 0;
333     for (i = 0;i < FcitxMessagesGetMessageCount(clientPreedit);i++) {
334         int type = FcitxMessagesGetClientMessageType(clientPreedit, i);
335         char* str = FcitxMessagesGetMessageString(clientPreedit, i);
336         XIMFeedback fb = 0;
337         if ((type & MSG_NOUNDERLINE) == 0)
338             fb |= XIMUnderline;
339         if (type & MSG_HIGHLIGHT)
340             fb |= XIMReverse;
341         unsigned int j;
342         unsigned int str_len = fcitx_utf8_strlen(str);
343         for (j = 0;j < str_len;j++) {
344             xim->feedback[offset] = fb;
345             offset++;
346         }
347     }
348     xim->feedback[len] = 0;
349 
350     IMPreeditCBStruct *pcb = fcitx_utils_new(IMPreeditCBStruct);
351     XIMText* text = fcitx_utils_new(XIMText);
352     pcb->major_code = XIM_PREEDIT_DRAW;
353     pcb->connect_id = ic->connect_id;
354     pcb->icid = ic->id;
355 
356     pcb->todo.draw.caret = fcitx_utf8_strnlen(preedit_string, cursorPos);
357     pcb->todo.draw.chg_first = 0;
358     pcb->todo.draw.chg_length = ic->onspot_preedit_length;
359     pcb->todo.draw.text = text;
360 
361     text->feedback = xim->feedback;
362 
363     Xutf8TextListToTextProperty(xim->display, (char**)&preedit_string,
364                                 1, XCompoundTextStyle, &tp);
365     text->encoding_is_wchar = 0;
366     text->length = strlen((char*)tp.value);
367     text->string.multi_byte = (char*)tp.value;
368     XimPendingCall(xim, XCT_CALLCALLBACK, (XPointer) pcb);
369     ic->onspot_preedit_length = len;
370 }
371 
372 // kate: indent-mode cstyle; space-indent on; indent-width 0;
373