1 /* Copyright (C) 2002-2005 RealVNC Ltd.  All Rights Reserved.
2  * Copyright 2011 Pierre Ossman <ossman@cendio.se> for Cendio AB
3  *
4  * This 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 software 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 software; if not, write to the Free Software
16  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307,
17  * USA.
18  */
19 
20 #include <windows.h>
21 #include <stdio.h>
22 
23 #define XK_MISCELLANY
24 #define XK_XKB_KEYS
25 #define XK_KOREAN
26 #include <rfb/keysymdef.h>
27 #include <rfb/XF86keysym.h>
28 
29 #include "keysym2ucs.h"
30 
31 #define NoSymbol 0
32 
33 // Missing in at least some versions of MinGW
34 #ifndef MAPVK_VK_TO_CHAR
35 #define MAPVK_VK_TO_CHAR 2
36 #endif
37 
38 int has_altgr;
39 HKL current_layout = 0;
40 
41 static HANDLE thread;
42 static DWORD thread_id;
43 
44 static HHOOK hook = 0;
45 static HWND target_wnd = 0;
46 
47 #define ARRAY_SIZE(a) (sizeof(a)/sizeof(*a))
48 
is_system_hotkey(int vkCode)49 static int is_system_hotkey(int vkCode) {
50   switch (vkCode) {
51   case VK_LWIN:
52   case VK_RWIN:
53   case VK_SNAPSHOT:
54     return 1;
55   case VK_TAB:
56     if (GetAsyncKeyState(VK_MENU) & 0x8000)
57       return 1;
58   case VK_ESCAPE:
59     if (GetAsyncKeyState(VK_MENU) & 0x8000)
60       return 1;
61     if (GetAsyncKeyState(VK_CONTROL) & 0x8000)
62       return 1;
63   }
64   return 0;
65 }
66 
keyboard_hook(int nCode,WPARAM wParam,LPARAM lParam)67 static LRESULT CALLBACK keyboard_hook(int nCode, WPARAM wParam, LPARAM lParam)
68 {
69   if (nCode >= 0) {
70     KBDLLHOOKSTRUCT* msgInfo = (KBDLLHOOKSTRUCT*)lParam;
71 
72     // Grabbing everything seems to mess up some keyboard state that
73     // FLTK relies on, so just grab the keys that we normally cannot.
74     if (is_system_hotkey(msgInfo->vkCode)) {
75       PostMessage(target_wnd, wParam, msgInfo->vkCode,
76                   (msgInfo->scanCode & 0xff) << 16 |
77                   (msgInfo->flags & 0xff) << 24);
78       return 1;
79     }
80   }
81 
82   return CallNextHookEx(hook, nCode, wParam, lParam);
83 }
84 
keyboard_thread(LPVOID data)85 static DWORD WINAPI keyboard_thread(LPVOID data)
86 {
87   MSG msg;
88 
89   target_wnd = (HWND)data;
90 
91   // Make sure a message queue is created
92   PeekMessage(&msg, NULL, 0, 0, PM_NOREMOVE | PM_NOYIELD);
93 
94   hook = SetWindowsHookEx(WH_KEYBOARD_LL, keyboard_hook, GetModuleHandle(0), 0);
95   // If something goes wrong then there is not much we can do.
96   // Just sit around and wait for WM_QUIT...
97 
98   while (GetMessage(&msg, NULL, 0, 0));
99 
100   if (hook)
101     UnhookWindowsHookEx(hook);
102 
103   target_wnd = 0;
104 
105   return 0;
106 }
107 
win32_enable_lowlevel_keyboard(HWND hwnd)108 int win32_enable_lowlevel_keyboard(HWND hwnd)
109 {
110   // Only one target at a time for now
111   if (thread != NULL) {
112     if (hwnd == target_wnd)
113       return 0;
114 
115     return 1;
116   }
117 
118   // We create a separate thread as it is crucial that hooks are processed
119   // in a timely manner.
120   thread = CreateThread(NULL, 0, keyboard_thread, hwnd, 0, &thread_id);
121   if (thread == NULL)
122     return 1;
123 
124   return 0;
125 }
126 
win32_disable_lowlevel_keyboard(HWND hwnd)127 void win32_disable_lowlevel_keyboard(HWND hwnd)
128 {
129   if (hwnd != target_wnd)
130     return;
131 
132   PostThreadMessage(thread_id, WM_QUIT, 0, 0);
133 
134   CloseHandle(thread);
135   thread = NULL;
136 }
137 
138 // Layout independent keys
139 static const int vkey_map[][3] = {
140   { VK_CANCEL,              NoSymbol,       XK_Break },
141   { VK_BACK,                XK_BackSpace,   NoSymbol },
142   { VK_TAB,                 XK_Tab,         NoSymbol },
143   { VK_CLEAR,               XK_Clear,       NoSymbol },
144   { VK_RETURN,              XK_Return,      XK_KP_Enter },
145   { VK_SHIFT,               XK_Shift_L,     NoSymbol },
146   { VK_CONTROL,             XK_Control_L,   XK_Control_R },
147   { VK_MENU,                XK_Alt_L,       XK_Alt_R },
148   { VK_PAUSE,               XK_Pause,       NoSymbol },
149   { VK_CAPITAL,             XK_Caps_Lock,   NoSymbol },
150   { VK_ESCAPE,              XK_Escape,      NoSymbol },
151   { VK_CONVERT,             XK_Henkan,      NoSymbol },
152   { VK_NONCONVERT,          XK_Muhenkan,    NoSymbol },
153   { VK_PRIOR,               XK_KP_Prior,    XK_Prior },
154   { VK_NEXT,                XK_KP_Next,     XK_Next },
155   { VK_END,                 XK_KP_End,      XK_End },
156   { VK_HOME,                XK_KP_Home,     XK_Home },
157   { VK_LEFT,                XK_KP_Left,     XK_Left },
158   { VK_UP,                  XK_KP_Up,       XK_Up },
159   { VK_RIGHT,               XK_KP_Right,    XK_Right },
160   { VK_DOWN,                XK_KP_Down,     XK_Down },
161   { VK_SNAPSHOT,            XK_Sys_Req,     XK_Print },
162   { VK_INSERT,              XK_KP_Insert,   XK_Insert },
163   { VK_DELETE,              XK_KP_Delete,   XK_Delete },
164   { VK_LWIN,                NoSymbol,       XK_Super_L },
165   { VK_RWIN,                NoSymbol,       XK_Super_R },
166   { VK_APPS,                NoSymbol,       XK_Menu },
167   { VK_SLEEP,               NoSymbol,       XF86XK_Sleep },
168   { VK_NUMPAD0,             XK_KP_0,        NoSymbol },
169   { VK_NUMPAD1,             XK_KP_1,        NoSymbol },
170   { VK_NUMPAD2,             XK_KP_2,        NoSymbol },
171   { VK_NUMPAD3,             XK_KP_3,        NoSymbol },
172   { VK_NUMPAD4,             XK_KP_4,        NoSymbol },
173   { VK_NUMPAD5,             XK_KP_5,        NoSymbol },
174   { VK_NUMPAD6,             XK_KP_6,        NoSymbol },
175   { VK_NUMPAD7,             XK_KP_7,        NoSymbol },
176   { VK_NUMPAD8,             XK_KP_8,        NoSymbol },
177   { VK_NUMPAD9,             XK_KP_9,        NoSymbol },
178   { VK_MULTIPLY,            XK_KP_Multiply, NoSymbol },
179   { VK_ADD,                 XK_KP_Add,      NoSymbol },
180   { VK_SUBTRACT,            XK_KP_Subtract, NoSymbol },
181   { VK_DIVIDE,              NoSymbol,       XK_KP_Divide },
182   /* VK_SEPARATOR and VK_DECIMAL left out on purpose. See further down. */
183   { VK_F1,                  XK_F1,          NoSymbol },
184   { VK_F2,                  XK_F2,          NoSymbol },
185   { VK_F3,                  XK_F3,          NoSymbol },
186   { VK_F4,                  XK_F4,          NoSymbol },
187   { VK_F5,                  XK_F5,          NoSymbol },
188   { VK_F6,                  XK_F6,          NoSymbol },
189   { VK_F7,                  XK_F7,          NoSymbol },
190   { VK_F8,                  XK_F8,          NoSymbol },
191   { VK_F9,                  XK_F9,          NoSymbol },
192   { VK_F10,                 XK_F10,         NoSymbol },
193   { VK_F11,                 XK_F11,         NoSymbol },
194   { VK_F12,                 XK_F12,         NoSymbol },
195   { VK_F13,                 XK_F13,         NoSymbol },
196   { VK_F14,                 XK_F14,         NoSymbol },
197   { VK_F15,                 XK_F15,         NoSymbol },
198   { VK_F16,                 XK_F16,         NoSymbol },
199   { VK_F17,                 XK_F17,         NoSymbol },
200   { VK_F18,                 XK_F18,         NoSymbol },
201   { VK_F19,                 XK_F19,         NoSymbol },
202   { VK_F20,                 XK_F20,         NoSymbol },
203   { VK_F21,                 XK_F21,         NoSymbol },
204   { VK_F22,                 XK_F22,         NoSymbol },
205   { VK_F23,                 XK_F23,         NoSymbol },
206   { VK_F24,                 XK_F24,         NoSymbol },
207   { VK_NUMLOCK,             NoSymbol,       XK_Num_Lock },
208   { VK_SCROLL,              XK_Scroll_Lock, NoSymbol },
209   { VK_BROWSER_BACK,        NoSymbol,       XF86XK_Back },
210   { VK_BROWSER_FORWARD,     NoSymbol,       XF86XK_Forward },
211   { VK_BROWSER_REFRESH,     NoSymbol,       XF86XK_Refresh },
212   { VK_BROWSER_STOP,        NoSymbol,       XF86XK_Stop },
213   { VK_BROWSER_SEARCH,      NoSymbol,       XF86XK_Search },
214   { VK_BROWSER_FAVORITES,   NoSymbol,       XF86XK_Favorites },
215   { VK_BROWSER_HOME,        NoSymbol,       XF86XK_HomePage },
216   { VK_VOLUME_MUTE,         NoSymbol,       XF86XK_AudioMute },
217   { VK_VOLUME_DOWN,         NoSymbol,       XF86XK_AudioLowerVolume },
218   { VK_VOLUME_UP,           NoSymbol,       XF86XK_AudioRaiseVolume },
219   { VK_MEDIA_NEXT_TRACK,    NoSymbol,       XF86XK_AudioNext },
220   { VK_MEDIA_PREV_TRACK,    NoSymbol,       XF86XK_AudioPrev },
221   { VK_MEDIA_STOP,          NoSymbol,       XF86XK_AudioStop },
222   { VK_MEDIA_PLAY_PAUSE,    NoSymbol,       XF86XK_AudioPlay },
223   { VK_LAUNCH_MAIL,         NoSymbol,       XF86XK_Mail },
224   { VK_LAUNCH_APP2,         NoSymbol,       XF86XK_Calculator },
225 };
226 
227 // Layout dependent keys, but without useful symbols
228 
229 // Japanese
230 static const int vkey_map_jp[][3] = {
231   { VK_KANA,                XK_Hiragana_Katakana, NoSymbol },
232   { VK_KANJI,               XK_Kanji,       NoSymbol },
233   { VK_OEM_ATTN,            XK_Eisu_toggle, NoSymbol },
234   { VK_OEM_FINISH,          XK_Katakana,    NoSymbol },
235   { VK_OEM_COPY,            XK_Hiragana,    NoSymbol },
236   // These are really XK_Zenkaku/XK_Hankaku but we have no way of
237   // keeping the client and server in sync
238   { VK_OEM_AUTO,            XK_Zenkaku_Hankaku, NoSymbol },
239   { VK_OEM_ENLW,            XK_Zenkaku_Hankaku, NoSymbol },
240   { VK_OEM_BACKTAB,         XK_Romaji,      NoSymbol },
241   { VK_ATTN,                XK_Romaji,      NoSymbol },
242 };
243 
244 // Korean
245 static const int vkey_map_ko[][3] = {
246   { VK_HANGUL,              XK_Hangul,      NoSymbol },
247   { VK_HANJA,               XK_Hangul_Hanja, NoSymbol },
248 };
249 
lookup_vkey_map(UINT vkey,int extended,const int map[][3],size_t size)250 static int lookup_vkey_map(UINT vkey, int extended, const int map[][3], size_t size)
251 {
252   size_t i;
253 
254   for (i = 0;i < size;i++) {
255     if (vkey != map[i][0])
256       continue;
257 
258     if (extended)
259       return map[i][2];
260     else
261       return map[i][1];
262   }
263 
264   return NoSymbol;
265 }
266 
win32_vkey_to_keysym(UINT vkey,int extended)267 int win32_vkey_to_keysym(UINT vkey, int extended)
268 {
269   HKL layout;
270   WORD lang, primary_lang;
271 
272   BYTE state[256];
273   int ret;
274   WCHAR wstr[10];
275 
276   // Start with keys that either don't generate a symbol, or
277   // generate the same symbol as some other key.
278 
279   ret = lookup_vkey_map(vkey, extended, vkey_map, ARRAY_SIZE(vkey_map));
280   if (ret != NoSymbol)
281     return ret;
282 
283   layout = GetKeyboardLayout(0);
284   lang = LOWORD(layout);
285   primary_lang = PRIMARYLANGID(lang);
286 
287   if (primary_lang == LANG_JAPANESE) {
288     ret = lookup_vkey_map(vkey, extended,
289                           vkey_map_jp, ARRAY_SIZE(vkey_map_jp));
290     if (ret != NoSymbol)
291       return ret;
292   }
293 
294   if (primary_lang == LANG_KOREAN) {
295     ret = lookup_vkey_map(vkey, extended,
296                           vkey_map_ko, ARRAY_SIZE(vkey_map_ko));
297     if (ret != NoSymbol)
298       return ret;
299   }
300 
301   // Windows is not consistent in which virtual key it uses for
302   // the numpad decimal key, and this is not likely to be fixed:
303   // http://blogs.msdn.com/michkap/archive/2006/09/13/752377.aspx
304   //
305   // To get X11 behaviour, we instead look at the text generated
306   // by they key.
307   if ((vkey == VK_DECIMAL) || (vkey == VK_SEPARATOR)) {
308     UINT ch;
309 
310     ch = MapVirtualKey(vkey, MAPVK_VK_TO_CHAR);
311     switch (ch) {
312     case ',':
313       return XK_KP_Separator;
314     case '.':
315       return XK_KP_Decimal;
316     default:
317       return NoSymbol;
318     }
319   }
320 
321   // MapVirtualKey() doesn't look at modifiers, so it is
322   // insufficient for mapping most keys to a symbol. ToUnicode()
323   // does what we want though. Unfortunately it keeps state, so
324   // we have to be careful around dead characters.
325 
326   GetKeyboardState(state);
327 
328   // Pressing Ctrl wreaks havoc with the symbol lookup, so turn
329   // that off. But AltGr shows up as Ctrl+Alt in Windows, so keep
330   // Ctrl if Alt is active.
331   if (!(state[VK_LCONTROL] & 0x80) || !(state[VK_RMENU] & 0x80))
332     state[VK_CONTROL] = state[VK_LCONTROL] = state[VK_RCONTROL] = 0;
333 
334   // FIXME: Multi character results, like U+0644 U+0627
335   //        on Arabic layout
336   ret = ToUnicode(vkey, 0, state, wstr, sizeof(wstr)/sizeof(wstr[0]), 0);
337 
338   if (ret == 0) {
339     // Most Ctrl+Alt combinations will fail to produce a symbol, so
340     // try it again with Ctrl unconditionally disabled.
341     state[VK_CONTROL] = state[VK_LCONTROL] = state[VK_RCONTROL] = 0;
342     ret = ToUnicode(vkey, 0, state, wstr, sizeof(wstr)/sizeof(wstr[0]), 0);
343   }
344 
345   if (ret == 1)
346     return ucs2keysym(wstr[0]);
347 
348   if (ret == -1) {
349     WCHAR dead_char;
350 
351     dead_char = wstr[0];
352 
353     // Need to clear out the state that the dead key has caused.
354     // This is the recommended method by Microsoft's engineers:
355     // http://blogs.msdn.com/b/michkap/archive/2007/10/27/5717859.aspx
356     do {
357       ret = ToUnicode(vkey, 0, state, wstr, sizeof(wstr)/sizeof(wstr[0]), 0);
358     } while (ret < 0);
359 
360     // Dead keys are represented by their spacing equivalent
361     // (or something similar depending on the layout)
362     return ucs2keysym(ucs2combining(dead_char));
363   }
364 
365   return NoSymbol;
366 }
367 
win32_has_altgr(void)368 int win32_has_altgr(void)
369 {
370   BYTE orig_state[256];
371   BYTE altgr_state[256];
372 
373   if (current_layout == GetKeyboardLayout(0))
374     return has_altgr;
375 
376   // Save current keyboard state so we can get things sane again after
377   // we're done
378   if (!GetKeyboardState(orig_state))
379     return 0;
380 
381   // We press Ctrl+Alt (Windows fake AltGr) and then test every key
382   // to see if it produces a printable character. If so then we assume
383   // AltGr is used in the current layout.
384 
385   has_altgr = 0;
386 
387   memset(altgr_state, 0, sizeof(altgr_state));
388   altgr_state[VK_CONTROL] = 0x80;
389   altgr_state[VK_MENU] = 0x80;
390 
391   for (UINT vkey = 0;vkey <= 0xff;vkey++) {
392     int ret;
393     WCHAR wstr[10];
394 
395     // Need to skip this one as it is a bit magical and will trigger
396     // a false positive
397     if (vkey == VK_PACKET)
398       continue;
399 
400     ret = ToUnicode(vkey, 0, altgr_state, wstr,
401                     sizeof(wstr)/sizeof(wstr[0]), 0);
402     if (ret == 1) {
403       has_altgr = 1;
404       break;
405     }
406 
407     if (ret == -1) {
408       // Dead key, need to clear out state before we proceed
409       do {
410         ret = ToUnicode(vkey, 0, altgr_state, wstr,
411                         sizeof(wstr)/sizeof(wstr[0]), 0);
412       } while (ret < 0);
413     }
414   }
415 
416   SetKeyboardState(orig_state);
417 
418   current_layout = GetKeyboardLayout(0);
419 
420   return has_altgr;
421 }
422