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