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