1 /***************************************************************************
2  *   Copyright (C) 2002~2005 by Yuking                                     *
3  *   yuking_net@sohu.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 #include <limits.h>
21 #include <ctype.h>
22 #include <X11/Xatom.h>
23 #include <X11/Xlib.h>
24 #include <cairo.h>
25 #include <libintl.h>
26 #include <cairo-xlib.h>
27 
28 #include "fcitx/fcitx.h"
29 #include "fcitx/module.h"
30 
31 #include "fcitx/ime.h"
32 #include "fcitx/instance.h"
33 #include "fcitx-utils/log.h"
34 #include "fcitx/frontend.h"
35 #include "fcitx-config/xdg.h"
36 #include "fcitx/hook.h"
37 #include "fcitx-utils/utils.h"
38 #include "module/x11/fcitx-x11.h"
39 #include "ui/cairostuff/cairostuff.h"
40 #include "ui/cairostuff/font.h"
41 #include "ui/classic/fcitx-classicui.h"
42 
43 #define VK_FILE "vk.conf"
44 
45 #define VK_WINDOW_WIDTH     354
46 #define VK_WINDOW_HEIGHT    164
47 #define VK_NUMBERS      47
48 #define VK_MAX          50
49 
50 const char* dummyTranslate[] = {
51     N_("Latin"),
52     N_("Fullwidth"),
53     N_("Greek"),
54     N_("Russian"),
55     N_("Index"),
56     N_("Math"),
57     N_("Number"),
58     N_("Special"),
59     N_("Hiragana"),
60     N_("Katakana"),
61     N_("Table Symbol")
62 };
63 
64 struct _FcitxVKState;
65 
66 typedef struct _VKS {
67     char            strSymbol[VK_NUMBERS][2][UTF8_MAX_LENGTH + 1]; //相应的符号
68     char           *strName;
69 } VKS;
70 
71 typedef struct _VKWindow {
72     Window          window;
73     int fontSize;
74     cairo_surface_t* surface;
75     cairo_surface_t* keyboard;
76     Display*        dpy;
77     struct _FcitxVKState* owner;
78     char *defaultFont;
79     int iVKWindowX;
80     int iVKWindowY;
81 } VKWindow;
82 
83 typedef struct _FcitxVKState {
84     VKWindow*       vkWindow;
85     int             iCurrentVK ;
86     int             iVKCount ;
87     VKS             vks[VK_MAX];
88     boolean         bShiftPressed;
89     boolean         bVKCaps;
90     boolean         bVK;
91     FcitxUIMenu     vkmenu;
92     FcitxInstance* owner;
93 } FcitxVKState;
94 
95 const char            vkTable[VK_NUMBERS + 1] = "`1234567890-=qwertyuiop[]\\asdfghjkl;'zxcvbnm,./";
96 const char            strCharTable[] = "`~1!2@3#4$5%6^7&8*9(0)-_=+[{]}\\|;:'\",<.>/?";    //用于转换上/下档键
97 
98 static boolean VKWindowEventHandler(void* arg, XEvent* event);
99 static void
100 VKInitWindowAttribute(FcitxVKState* vkstate, Visual ** vs, Colormap * cmap,
101                       XSetWindowAttributes * attrib,
102                       unsigned long *attribmask, int *depth);
103 static Visual * VKFindARGBVisual(FcitxVKState* vkstate);
104 static void VKSetWindowProperty(FcitxVKState* vkstate, Window window, FcitxXWindowType type, char *windowTitle);
105 static boolean VKMouseClick(FcitxVKState* vkstate, Window window, int *x, int *y);
106 static void SwitchVK(FcitxVKState *vkstate);
107 static void LoadVKMapFile(FcitxVKState *vkstate);
108 static void ChangVK(FcitxVKState* vkstate);
109 static void ReloadVK(void *arg);
110 static int MyToUpper(int iChar);
111 static int MyToLower(int iChar);
112 static cairo_surface_t* LoadVKImage(VKWindow* vkWindow);
113 static void *VKCreate(FcitxInstance* instance);
114 static VKWindow* CreateVKWindow(FcitxVKState* vkstate);
115 static boolean GetVKState(void *arg);
116 static void ToggleVKState(void *arg);
117 static INPUT_RETURN_VALUE ToggleVKStateWithHotkey(void* arg);
118 static void DrawVKWindow(VKWindow* vkWindow);
119 static boolean VKMouseKey(FcitxVKState* vkstate, int x, int y);
120 static boolean VKPreFilter(void* arg, FcitxKeySym sym,
121                            unsigned int state,
122                            INPUT_RETURN_VALUE *retval
123                           );
124 static  void VKReset(void* arg);
125 static void VKUpdate(void* arg);
126 static INPUT_RETURN_VALUE DoVKInput(FcitxVKState* vkstate, KeySym sym, int state);
127 static void DisplayVKWindow(VKWindow* vkWindow);
128 static boolean VKMenuAction(FcitxUIMenu *menu, int index);
129 static void UpdateVKMenu(FcitxUIMenu *menu);
130 static void SelectVK(FcitxVKState* vkstate, int vkidx);
131 
132 static FcitxConfigColor blackColor = {0, 0, 0};
133 
134 FCITX_DEFINE_PLUGIN(fcitx_vk, module, FcitxModule) = {
135     VKCreate,
136     NULL,
137     NULL,
138     NULL,
139     ReloadVK
140 };
141 
VKCreate(FcitxInstance * instance)142 void *VKCreate(FcitxInstance* instance)
143 {
144     FcitxVKState *vkstate = fcitx_utils_malloc0(sizeof(FcitxVKState));
145     FcitxGlobalConfig* config = FcitxInstanceGetGlobalConfig(instance);
146     vkstate->owner = instance;
147 
148     FcitxHotkeyHook hotkey;
149     hotkey.hotkey = config->hkVK;
150     hotkey.hotkeyhandle = ToggleVKStateWithHotkey;
151     hotkey.arg = vkstate;
152     FcitxInstanceRegisterHotkeyFilter(instance, hotkey);
153 
154     FcitxUIRegisterStatus(instance, vkstate, "vk", _("Toggle Virtual Keyboard"), _("Virtual Keyboard State"),  ToggleVKState, GetVKState);
155 
156     LoadVKMapFile(vkstate);
157 
158     FcitxKeyFilterHook hk;
159     hk.arg = vkstate ;
160     hk.func = VKPreFilter;
161     FcitxInstanceRegisterPreInputFilter(instance, hk);
162 
163     hk.arg = &vkstate->bVK;
164     hk.func = FcitxDummyReleaseInputHook;
165     FcitxInstanceRegisterPreReleaseInputFilter(instance, hk);
166 
167     FcitxIMEventHook resethk;
168     resethk.arg = vkstate;
169     resethk.func = VKReset;
170     FcitxInstanceRegisterTriggerOnHook(instance, resethk);
171     FcitxInstanceRegisterTriggerOffHook(instance, resethk);
172 
173     resethk.func = VKUpdate;
174     FcitxInstanceRegisterInputFocusHook(instance, resethk);
175     FcitxInstanceRegisterInputUnFocusHook(instance, resethk);
176 
177     FcitxMenuInit(&vkstate->vkmenu);
178     vkstate->vkmenu.candStatusBind = strdup("vk");
179     vkstate->vkmenu.name = strdup(_("Virtual Keyboard"));
180 
181     vkstate->vkmenu.UpdateMenu = UpdateVKMenu;
182     vkstate->vkmenu.MenuAction = VKMenuAction;
183     vkstate->vkmenu.priv = vkstate;
184     vkstate->vkmenu.isSubMenu = false;
185 
186     FcitxUIRegisterMenu(instance, &vkstate->vkmenu);
187 
188     return vkstate;
189 }
190 
VKMenuAction(FcitxUIMenu * menu,int index)191 boolean VKMenuAction(FcitxUIMenu *menu, int index)
192 {
193     FcitxVKState* vkstate = (FcitxVKState*) menu->priv;
194     if (index < vkstate->iVKCount)
195         SelectVK(vkstate, index);
196     else {
197         if (vkstate->bVK) {
198             FcitxUIUpdateStatus(vkstate->owner, "vk");
199         }
200     }
201     return true;
202 }
203 
UpdateVKMenu(FcitxUIMenu * menu)204 void UpdateVKMenu(FcitxUIMenu *menu)
205 {
206     FcitxVKState* vkstate = (FcitxVKState*) menu->priv;
207     FcitxMenuClear(menu);
208     int i;
209     for (i = 0; i < vkstate->iVKCount; i ++)
210         FcitxMenuAddMenuItem(&vkstate->vkmenu, vkstate->vks[i].strName, MENUTYPE_SIMPLE, NULL);
211     if (vkstate->bVK) {
212         FcitxMenuAddMenuItem(&vkstate->vkmenu, _("Close virtual keyboard"), MENUTYPE_SIMPLE, NULL);
213     }
214     menu->mark = vkstate->iCurrentVK;
215 }
216 
VKReset(void * arg)217 void VKReset(void* arg)
218 {
219     FcitxVKState *vkstate = (FcitxVKState*) arg;
220     VKWindow* vkWindow = vkstate->vkWindow;
221     if (vkstate->bVK != false)
222         FcitxUIUpdateStatus(vkstate->owner, "vk");
223     if (vkWindow)
224         XUnmapWindow(vkWindow->dpy, vkWindow->window);
225 }
226 
VKUpdate(void * arg)227 void VKUpdate(void* arg)
228 {
229     FcitxVKState *vkstate = (FcitxVKState*) arg;
230     VKWindow* vkWindow = vkstate->vkWindow;
231     if (vkWindow) {
232 
233         if (FcitxInstanceGetCurrentState(vkstate->owner) != IS_CLOSED && vkstate->bVK) {
234             DrawVKWindow(vkWindow);
235             DisplayVKWindow(vkWindow);
236         } else
237             XUnmapWindow(vkWindow->dpy, vkWindow->window);
238     }
239 }
240 
VKPreFilter(void * arg,FcitxKeySym sym,unsigned int state,INPUT_RETURN_VALUE * retval)241 boolean VKPreFilter(void* arg, FcitxKeySym sym, unsigned int state, INPUT_RETURN_VALUE* retval)
242 {
243     FcitxVKState *vkstate = (FcitxVKState*) arg;
244     if (vkstate->bVK) {
245         INPUT_RETURN_VALUE ret = DoVKInput(vkstate, sym, state);
246         *retval = ret;
247         return true;
248     }
249     return false;
250 }
251 
GetVKState(void * arg)252 boolean GetVKState(void *arg)
253 {
254     FcitxVKState *vkstate = (FcitxVKState*) arg;
255     return vkstate->bVK;
256 }
257 
ToggleVKState(void * arg)258 void ToggleVKState(void *arg)
259 {
260     FcitxVKState *vkstate = (FcitxVKState*) arg;
261     SwitchVK(vkstate);
262 }
263 
ToggleVKStateWithHotkey(void * arg)264 INPUT_RETURN_VALUE ToggleVKStateWithHotkey(void* arg)
265 {
266     FcitxVKState *vkstate = (FcitxVKState*) arg;
267     FcitxUIUpdateStatus(vkstate->owner, "vk");
268     return IRV_DO_NOTHING;
269 }
270 
CreateVKWindow(FcitxVKState * vkstate)271 VKWindow* CreateVKWindow(FcitxVKState* vkstate)
272 {
273     XSetWindowAttributes attrib;
274     unsigned long   attribmask;
275     char        strWindowName[] = "Fcitx VK Window";
276     Colormap cmap;
277     Visual * vs;
278     int depth;
279     VKWindow* vkWindow = fcitx_utils_new(VKWindow);
280     vkWindow->owner = vkstate;
281 
282     LoadVKImage(vkWindow);
283 
284     vs = VKFindARGBVisual(vkstate);
285     VKInitWindowAttribute(vkstate, &vs, &cmap, &attrib, &attribmask, &depth);
286     vkWindow->dpy = FcitxX11GetDisplay(vkstate->owner);
287 
288     vkWindow->fontSize = 12;
289     vkWindow->defaultFont = strdup("sans");
290 #ifndef _ENABLE_PANGO
291     GetValidFont("zh", &vkWindow->defaultFont);
292 #endif
293 
294     vkWindow->window = XCreateWindow(vkWindow->dpy,
295                                      DefaultRootWindow(vkWindow->dpy),
296                                      0, 0,
297                                      VK_WINDOW_WIDTH, VK_WINDOW_HEIGHT,
298                                      0, depth, InputOutput, vs, attribmask, &attrib);
299     if (vkWindow->window == (Window) None)
300         return NULL;
301 
302     vkWindow->surface = cairo_xlib_surface_create(vkWindow->dpy, vkWindow->window, vs, VK_WINDOW_WIDTH, VK_WINDOW_HEIGHT);
303 
304     XSelectInput(vkWindow->dpy, vkWindow->window, ExposureMask | ButtonPressMask | ButtonReleaseMask  | PointerMotionMask);
305 
306     VKSetWindowProperty(vkstate, vkWindow->window, FCITX_WINDOW_DOCK, strWindowName);
307 
308     FcitxX11AddXEventHandler(vkstate->owner, VKWindowEventHandler, vkWindow);
309 
310     return vkWindow;
311 }
312 
VKWindowEventHandler(void * arg,XEvent * event)313 boolean VKWindowEventHandler(void* arg, XEvent* event)
314 {
315     VKWindow* vkWindow = arg;
316     if (event->xany.window == vkWindow->window) {
317         switch (event->type) {
318         case Expose:
319             DrawVKWindow(vkWindow);
320             break;
321         case ButtonPress:
322             switch (event->xbutton.button) {
323             case Button1: {
324                 if (!VKMouseKey(vkWindow->owner, event->xbutton.x, event->xbutton.y)) {
325                     vkWindow->iVKWindowX = event->xbutton.x;
326                     vkWindow->iVKWindowY = event->xbutton.y;
327                     VKMouseClick(vkWindow->owner, vkWindow->window, &vkWindow->iVKWindowX, &vkWindow->iVKWindowY);
328                     DrawVKWindow(vkWindow);
329                 }
330             }
331             break;
332             }
333             break;
334         }
335         return true;
336     }
337 
338     return false;
339 }
340 
LoadVKImage(VKWindow * vkWindow)341 cairo_surface_t* LoadVKImage(VKWindow* vkWindow)
342 {
343     FcitxVKState* vkstate = vkWindow->owner;
344     boolean fallback = true;
345     char vkimage[] = "keyboard.png";
346     cairo_surface_t *image = FcitxClassicUILoadImage(vkstate->owner,
347                                                      vkimage, &fallback);
348 
349     if (image)
350         return image;
351 
352     if (!vkWindow->keyboard) {
353         char* path = fcitx_utils_get_fcitx_path_with_filename("pkgdatadir", "skin/default/keyboard.png");
354         if (fcitx_utils_isreg(path)) {
355             vkWindow->keyboard = cairo_image_surface_create_from_png(path);
356         }
357         free(path);
358     }
359     return vkWindow->keyboard;
360 }
361 
DisplayVKWindow(VKWindow * vkWindow)362 void DisplayVKWindow(VKWindow* vkWindow)
363 {
364     XMapRaised(vkWindow->dpy, vkWindow->window);
365 }
366 
DestroyVKWindow(VKWindow * vkWindow)367 void DestroyVKWindow(VKWindow* vkWindow)
368 {
369     cairo_surface_destroy(vkWindow->surface);
370     XDestroyWindow(vkWindow->dpy, vkWindow->window);
371 }
372 
DrawVKWindow(VKWindow * vkWindow)373 void DrawVKWindow(VKWindow* vkWindow)
374 {
375     int i;
376     int iPos;
377     cairo_t *cr;
378     FcitxVKState *vkstate = vkWindow->owner;
379     VKS *vks = vkstate->vks;
380 
381     FcitxConfigColor *fontColor;
382     fontColor = FcitxClassicUIGetKeyboardFontColor(vkstate->owner);
383     char **font = FcitxClassicUIGetFont(vkstate->owner);
384 
385     if (!fontColor || !font) {
386         fontColor = &blackColor;
387         font = &vkWindow->defaultFont;
388     }
389 
390     cr = cairo_create(vkWindow->surface);
391     cairo_surface_t* vkimage = LoadVKImage(vkWindow);
392     if (vkimage) {
393         cairo_set_source_surface(cr, vkimage, 0, 0);
394     } else {
395         cairo_set_source_rgb(cr, 1, 1, 1);
396     }
397     cairo_paint(cr);
398 
399     FcitxCairoTextContext* ctc = FcitxCairoTextContextCreate(cr);
400     FcitxCairoTextContextSet(ctc, *font, vkWindow->fontSize, 0);
401     /* 显示字符 */
402     /* 名称 */
403     FcitxCairoTextContextOutputString(ctc, vks[vkstate->iCurrentVK].strName, (VK_WINDOW_WIDTH - FcitxCairoTextContextStringWidth(ctc, vks[vkstate->iCurrentVK].strName)) / 2, 6, fontColor);
404 
405     /* 第一排 */
406     iPos = 13;
407     for (i = 0; i < 13; i++) {
408         FcitxCairoTextContextOutputString(ctc, vks[vkstate->iCurrentVK].strSymbol[i][1], iPos, 27, fontColor);
409         FcitxCairoTextContextOutputString(ctc, vks[vkstate->iCurrentVK].strSymbol[i][0], iPos - 5, 40, fontColor);
410         iPos += 24;
411     }
412     /* 第二排 */
413     iPos = 48;
414     for (i = 13; i < 26; i++) {
415         FcitxCairoTextContextOutputString(ctc, vks[vkstate->iCurrentVK].strSymbol[i][1], iPos, 55, fontColor);
416         FcitxCairoTextContextOutputString(ctc, vks[vkstate->iCurrentVK].strSymbol[i][0], iPos - 5, 68, fontColor);
417         iPos += 24;
418     }
419     /* 第三排 */
420     iPos = 55;
421     for (i = 26; i < 37; i++) {
422         FcitxCairoTextContextOutputString(ctc, vks[vkstate->iCurrentVK].strSymbol[i][1], iPos, 83, fontColor);
423         FcitxCairoTextContextOutputString(ctc, vks[vkstate->iCurrentVK].strSymbol[i][0], iPos - 5, 96, fontColor);
424         iPos += 24;
425     }
426 
427     /* 第四排 */
428     iPos = 72;
429     for (i = 37; i < 47; i++) {
430         FcitxCairoTextContextOutputString(ctc, vks[vkstate->iCurrentVK].strSymbol[i][1], iPos, 111, fontColor);
431         FcitxCairoTextContextOutputString(ctc, vks[vkstate->iCurrentVK].strSymbol[i][0], iPos - 5, 124, fontColor);
432         iPos += 24;
433     }
434 
435     cairo_destroy(cr);
436     cairo_surface_flush(vkWindow->surface);
437 }
438 
439 /*
440  * 处理相关鼠标键
441  */
VKMouseKey(FcitxVKState * vkstate,int x,int y)442 boolean VKMouseKey(FcitxVKState* vkstate, int x, int y)
443 {
444     int             iIndex = 0;
445     char            strKey[3] = { 0, 0, 0};
446     char           *pstr = NULL;
447     FcitxInstance* instance = vkstate->owner;
448 
449     if (FcitxUIIsInBox(x, y, 1, 1, VK_WINDOW_WIDTH, 16))
450         ChangVK(vkstate);
451     else {
452         if (FcitxInstanceGetCurrentIC(instance) == NULL)
453             return false;
454 
455         strKey[1] = '\0';
456         pstr = strKey;
457         if (y >= 28 && y <= 55) {   //第一行
458             if (x < 4 || x > 348)
459                 return false;
460 
461             x -= 4;
462             if (x >= 313 && x <= 344) { //backspace
463                 FcitxInstanceForwardKey(instance, FcitxInstanceGetCurrentIC(instance), FCITX_PRESS_KEY, FcitxKey_BackSpace, 0);
464                 return true;
465             } else {
466                 iIndex = x / 24;
467                 if (iIndex > 12)    //避免出现错误
468                     iIndex = 12;
469                 pstr = vkstate->vks[vkstate->iCurrentVK].strSymbol[iIndex][vkstate->bShiftPressed ^ vkstate->bVKCaps];
470                 if (vkstate->bShiftPressed) {
471                     vkstate->bShiftPressed = false;
472                     DrawVKWindow(vkstate->vkWindow);
473                 }
474             }
475         } else if (y >= 56 && y <= 83) { //第二行
476             if (x < 4 || x > 350)
477                 return false;
478 
479             if (x >= 4 && x < 38) { //Tab
480                 FcitxInstanceForwardKey(instance, FcitxInstanceGetCurrentIC(instance), FCITX_PRESS_KEY, FcitxKey_Tab, 0);
481                 return true;
482             } else {
483                 iIndex = 13 + (x - 38) / 24;
484                 pstr = vkstate->vks[vkstate->iCurrentVK].strSymbol[iIndex][vkstate->bShiftPressed ^ vkstate->bVKCaps];
485                 if (vkstate->bShiftPressed) {
486                     vkstate->bShiftPressed = false;
487                     DrawVKWindow(vkstate->vkWindow);
488                 }
489             }
490         } else if (y >= 84 && y <= 111) { //第三行
491             if (x < 4 || x > 350)
492                 return false;
493 
494             if (x >= 4 && x < 44) { //Caps
495                 //改变大写键状态
496                 vkstate->bVKCaps = !vkstate->bVKCaps;
497                 pstr = (char *) NULL;
498                 DrawVKWindow(vkstate->vkWindow);
499             } else if (x > 308 && x <= 350) //Return
500                 strKey[0] = '\n';
501             else {
502                 iIndex = 26 + (x - 44) / 24;
503                 pstr = vkstate->vks[vkstate->iCurrentVK].strSymbol[iIndex][vkstate->bShiftPressed ^ vkstate->bVKCaps];
504                 if (vkstate->bShiftPressed) {
505                     vkstate->bShiftPressed = false;
506                     DrawVKWindow(vkstate->vkWindow);
507                 }
508             }
509         } else if (y >= 112 && y <= 139) {  //第四行
510             if (x < 4 || x > 302)
511                 return false;
512 
513             if (x >= 4 && x < 62) { //SHIFT
514                 //改变SHIFT键状态
515                 vkstate->bShiftPressed = !vkstate->bShiftPressed;
516                 pstr = (char *) NULL;
517                 DrawVKWindow(vkstate->vkWindow);
518             } else {
519                 iIndex = 37 + (x - 62) / 24;
520                 pstr = vkstate->vks[vkstate->iCurrentVK].strSymbol[iIndex][vkstate->bShiftPressed ^ vkstate->bVKCaps];
521                 if (vkstate->bShiftPressed) {
522                     vkstate->bShiftPressed = false;
523                     DrawVKWindow(vkstate->vkWindow);
524                 }
525             }
526         } else if (y >= 140 && y <= 162) {  //第五行
527             if (x >= 4 && x < 38) { //Ins
528                 //改变INS键状态
529                 FcitxInstanceForwardKey(instance, FcitxInstanceGetCurrentIC(instance), FCITX_PRESS_KEY, FcitxKey_Insert, 0);
530                 return true;
531             } else if (x >= 61 && x < 98) { //DEL
532                 FcitxInstanceForwardKey(instance, FcitxInstanceGetCurrentIC(instance), FCITX_PRESS_KEY, FcitxKey_Delete, 0);
533                 return true;
534             } else if (x >= 99 && x < 270)  //空格
535                 strcpy(strKey, " ");
536             else if (x >= 312 && x <= 350) {    //ESC
537                 SwitchVK(vkstate);
538                 pstr = (char *) NULL;
539             } else
540                 return false;
541         }
542 
543         if (pstr) {
544             FcitxInstanceCommitString(instance, FcitxInstanceGetCurrentIC(instance), pstr);
545         }
546     }
547 
548     return true;
549 }
550 
551 /*
552  * 读取虚拟键盘映射文件
553  */
LoadVKMapFile(FcitxVKState * vkstate)554 void LoadVKMapFile(FcitxVKState *vkstate)
555 {
556     int             i, j;
557     FILE           *fp;
558     char           *buf = NULL;
559     char           *pstr;
560     VKS*            vks = vkstate->vks;
561     size_t          len;
562 
563     for (j = 0; j < VK_MAX; j++) {
564         for (i = 0; i < VK_NUMBERS; i++) {
565             vks[j].strSymbol[i][0][0] = '\0';
566             vks[j].strSymbol[i][1][0] = '\0';
567         }
568         if (vks[j].strName) {
569             free(vks[j].strName);
570             vks[j].strName = NULL;
571         }
572     }
573 
574     fp = FcitxXDGGetFileWithPrefix("data", VK_FILE, "r", NULL);
575 
576     if (!fp)
577         return;
578 
579     vkstate->iVKCount = 0;
580 
581     while (getline(&buf, &len, fp) != -1) {
582         pstr = buf;
583         while (*pstr == ' ' || *pstr == '\t')
584             pstr++;
585         if (pstr[0] == '#')
586             continue;
587 
588         i = strlen(pstr) - 1;
589         if (pstr[i] == '\n')
590             pstr[i] = '\0';
591         if (!strlen(pstr))
592             continue;
593 
594         if (!strcmp(pstr, "[VK]"))
595             vkstate->iVKCount++;
596         else if (!strncmp(pstr, "NAME=", 5))
597             vks[vkstate->iVKCount - 1].strName = strdup(gettext(pstr + 5));
598         else {
599             if (pstr[1] != '=' && !vkstate->iVKCount)
600                 continue;
601 
602             for (i = 0; i < VK_NUMBERS; i++) {
603                 if (vkTable[i] == tolower(pstr[0])) {
604                     pstr += 2;
605                     while (*pstr == ' ' || *pstr == '\t')
606                         pstr++;
607 
608                     if (!(*pstr))
609                         break;
610 
611                     j = 0;
612                     while (*pstr && (*pstr != ' ' && *pstr != '\t'))
613                         vks[vkstate->iVKCount - 1].strSymbol[i][0][j++] = *pstr++;
614                     vks[vkstate->iVKCount - 1].strSymbol[i][0][j] = '\0';
615 
616                     j = 0;
617                     while (*pstr == ' ' || *pstr == '\t')
618                         pstr++;
619                     if (*pstr) {
620                         while (*pstr && (*pstr != ' ' && *pstr != '\t'))
621                             vks[vkstate->iVKCount - 1].strSymbol[i][1][j++] = *pstr++;
622                         vks[vkstate->iVKCount - 1].strSymbol[i][1][j] = '\0';
623                     }
624 
625                     break;
626                 }
627             }
628         }
629     }
630 
631     if (buf)
632         free(buf);
633 
634     fclose(fp);
635 }
636 
637 /*
638  * 根据字符查找符号
639  */
VKGetSymbol(FcitxVKState * vkstate,char cChar)640 char           *VKGetSymbol(FcitxVKState *vkstate, char cChar)
641 {
642     int             i;
643 
644     for (i = 0; i < VK_NUMBERS; i++) {
645         if (MyToUpper(vkTable[i]) == cChar)
646             return vkstate->vks[vkstate->iCurrentVK].strSymbol[i][1];
647         if (MyToLower(vkTable[i]) == cChar)
648             return vkstate->vks[vkstate->iCurrentVK].strSymbol[i][0];
649     }
650 
651     return NULL;
652 }
653 
654 /*
655  * 上/下档键字符转换,以取代toupper和tolower
656  */
MyToUpper(int iChar)657 int MyToUpper(int iChar)
658 {
659     const char           *pstr;
660 
661     pstr = strCharTable;
662     while (*pstr) {
663         if (*pstr == iChar)
664             return *(pstr + 1);
665         pstr += 2;
666     }
667 
668     return toupper(iChar);
669 }
670 
MyToLower(int iChar)671 int MyToLower(int iChar)
672 {
673     const char           *pstr;
674 
675     pstr = strCharTable + 1;
676     for (;;) {
677         if (*pstr == iChar)
678             return *(pstr - 1);
679         if (!(*(pstr + 1)))
680             break;
681         pstr += 2;
682     }
683 
684     return tolower(iChar);
685 }
686 
ChangVK(FcitxVKState * vkstate)687 void ChangVK(FcitxVKState* vkstate)
688 {
689     vkstate->iCurrentVK++;
690     if (vkstate->iCurrentVK == vkstate->iVKCount)
691         vkstate->iCurrentVK = 0;
692 
693     vkstate->bVKCaps = false;
694     vkstate->bShiftPressed = false;
695 
696     DrawVKWindow(vkstate->vkWindow);
697 }
698 
DoVKInput(FcitxVKState * vkstate,KeySym sym,int state)699 INPUT_RETURN_VALUE DoVKInput(FcitxVKState* vkstate, KeySym sym, int state)
700 {
701     char           *pstr = NULL;
702     FcitxInputState *input = FcitxInstanceGetInputState(vkstate->owner);
703 
704     if (FcitxHotkeyIsHotKeySimple(sym, state))
705         pstr = VKGetSymbol(vkstate, sym);
706     if (!pstr)
707         return IRV_TO_PROCESS;
708     else {
709         strcpy(FcitxInputStateGetOutputString(input), pstr);
710         return IRV_COMMIT_STRING;
711     }
712 }
713 
SwitchVK(FcitxVKState * vkstate)714 void SwitchVK(FcitxVKState *vkstate)
715 {
716     FcitxInstance* instance = vkstate->owner;
717     if (vkstate->vkWindow == NULL)
718         vkstate->vkWindow = CreateVKWindow(vkstate);
719     VKWindow *vkWindow = vkstate->vkWindow;
720     if (!vkstate->iVKCount)
721         return;
722 
723     vkstate->bVK = !vkstate->bVK;
724 
725     if (vkstate->bVK) {
726         int             x, y;
727         FcitxRect rect;
728         int icx = 0, icy = 0, w = 0, h = 0;
729         FcitxInputContext* ic = FcitxInstanceGetCurrentIC(instance);
730         FcitxInstanceGetWindowRect(instance, ic, &icx, &icy, &w, &h);
731         FcitxX11GetScreenGeometry(instance, &icx, &icy, &rect);
732         x = (rect.x1 + rect.x2) / 2 - VK_WINDOW_WIDTH / 2;
733         y = rect.y1 + 40;
734 
735         if ((y + VK_WINDOW_HEIGHT) >= rect.y2)
736             y = rect.y2 - VK_WINDOW_HEIGHT - 2;
737         if (y < rect.y1)
738             y = rect.y1;
739         if ((x + VK_WINDOW_WIDTH) >= rect.x2)
740             x = rect.x2 - VK_WINDOW_WIDTH - 1;
741         if (x < rect.x1)
742             x = rect.x1;
743 
744 
745         XMoveWindow(vkWindow->dpy, vkWindow->window, x, y);
746         DisplayVKWindow(vkWindow);
747         FcitxUICloseInputWindow(instance);
748 
749         if (ic && FcitxInstanceGetCurrentState(instance) == IS_CLOSED)
750             FcitxInstanceEnableIM(instance, ic, true);
751     } else {
752         XUnmapWindow(vkWindow->dpy, vkWindow->window);
753         FcitxInstanceCleanInputWindow(instance);
754         FcitxUIUpdateInputWindow(instance);
755     }
756 }
757 
758 /*
759 *选择指定index的虚拟键盘
760 */
SelectVK(FcitxVKState * vkstate,int vkidx)761 void SelectVK(FcitxVKState* vkstate, int vkidx)
762 {
763     vkstate->bVK = false;
764     vkstate->iCurrentVK = vkidx;
765     FcitxUIUpdateStatus(vkstate->owner, "vk");
766     if (vkstate->vkWindow)
767         DrawVKWindow(vkstate->vkWindow);
768 }
769 
770 void
VKInitWindowAttribute(FcitxVKState * vkstate,Visual ** vs,Colormap * cmap,XSetWindowAttributes * attrib,unsigned long * attribmask,int * depth)771 VKInitWindowAttribute(FcitxVKState* vkstate, Visual ** vs, Colormap * cmap,
772                       XSetWindowAttributes * attrib,
773                       unsigned long *attribmask, int *depth)
774 {
775     FcitxX11InitWindowAttribute(vkstate->owner,
776                                 vs, cmap, attrib, attribmask, depth);
777 }
778 
VKFindARGBVisual(FcitxVKState * vkstate)779 Visual * VKFindARGBVisual(FcitxVKState* vkstate)
780 {
781     return FcitxX11FindARGBVisual(vkstate->owner);
782 }
783 
VKSetWindowProperty(FcitxVKState * vkstate,Window window,FcitxXWindowType type,char * windowTitle)784 void VKSetWindowProperty(FcitxVKState* vkstate, Window window, FcitxXWindowType type, char *windowTitle)
785 {
786     FcitxX11SetWindowProp(vkstate->owner, &window, &type, windowTitle);
787 }
788 
789 boolean
VKMouseClick(FcitxVKState * vkstate,Window window,int * x,int * y)790 VKMouseClick(FcitxVKState* vkstate, Window window, int *x, int *y)
791 {
792     boolean bMoved = false;
793     FcitxX11MouseClick(vkstate->owner, &window, x, y, &bMoved);
794     return bMoved;
795 }
796 
ReloadVK(void * arg)797 void ReloadVK(void* arg)
798 {
799     FcitxVKState* vkstate = (FcitxVKState*)arg;
800     LoadVKMapFile(vkstate);
801 }
802 
803 
804 // kate: indent-mode cstyle; space-indent on; indent-width 0;
805