20 #include <freerdp/config.h>
26 #include <winpr/assert.h>
27 #include <winpr/crt.h>
29 #include <freerdp/utils/string.h>
30 #include <freerdp/types.h>
31 #include <freerdp/locale/keyboard.h>
32 #include <freerdp/locale/locale.h>
34 #include <freerdp/log.h>
36 #include "liblocale.h"
38 #if defined(__MACOSX__)
39 #include "keyboard_apple.h"
42 #define TAG FREERDP_TAG("locale.keyboard")
45 #include "keyboard_x11.h"
48 #include "keyboard_xkbfile.h"
53 static WINPR_KEYCODE_TYPE maptype = WINPR_KEYCODE_TYPE_NONE;
54 static DWORD VIRTUAL_SCANCODE_TO_X11_KEYCODE[256][2] = { 0 };
55 static DWORD X11_KEYCODE_TO_VIRTUAL_SCANCODE[256] = { 0 };
56 static DWORD REMAPPING_TABLE[0x10000] = { 0 };
58 struct scancode_map_entry
64 static const struct scancode_map_entry RDP_SCANCODE_MAP[] = {
65 { RDP_SCANCODE_ESCAPE,
"VK_ESCAPE" },
66 { RDP_SCANCODE_KEY_1,
"VK_KEY_1" },
67 { RDP_SCANCODE_KEY_2,
"VK_KEY_2" },
68 { RDP_SCANCODE_KEY_3,
"VK_KEY_3" },
69 { RDP_SCANCODE_KEY_4,
"VK_KEY_4" },
70 { RDP_SCANCODE_KEY_5,
"VK_KEY_5" },
71 { RDP_SCANCODE_KEY_6,
"VK_KEY_6" },
72 { RDP_SCANCODE_KEY_7,
"VK_KEY_7" },
73 { RDP_SCANCODE_KEY_8,
"VK_KEY_8" },
74 { RDP_SCANCODE_KEY_9,
"VK_KEY_9" },
75 { RDP_SCANCODE_KEY_0,
"VK_KEY_0" },
76 { RDP_SCANCODE_OEM_MINUS,
"VK_OEM_MINUS" },
77 { RDP_SCANCODE_OEM_PLUS,
"VK_OEM_PLUS" },
78 { RDP_SCANCODE_BACKSPACE,
"VK_BACK Backspace" },
79 { RDP_SCANCODE_TAB,
"VK_TAB" },
80 { RDP_SCANCODE_KEY_Q,
"VK_KEY_Q" },
81 { RDP_SCANCODE_KEY_W,
"VK_KEY_W" },
82 { RDP_SCANCODE_KEY_E,
"VK_KEY_E" },
83 { RDP_SCANCODE_KEY_R,
"VK_KEY_R" },
84 { RDP_SCANCODE_KEY_T,
"VK_KEY_T" },
85 { RDP_SCANCODE_KEY_Y,
"VK_KEY_Y" },
86 { RDP_SCANCODE_KEY_U,
"VK_KEY_U" },
87 { RDP_SCANCODE_KEY_I,
"VK_KEY_I" },
88 { RDP_SCANCODE_KEY_O,
"VK_KEY_O" },
89 { RDP_SCANCODE_KEY_P,
"VK_KEY_P" },
90 { RDP_SCANCODE_OEM_4,
"VK_OEM_4 '[' on US" },
91 { RDP_SCANCODE_OEM_6,
"VK_OEM_6 ']' on US" },
92 { RDP_SCANCODE_RETURN,
"VK_RETURN Normal Enter" },
93 { RDP_SCANCODE_LCONTROL,
"VK_LCONTROL" },
94 { RDP_SCANCODE_KEY_A,
"VK_KEY_A" },
95 { RDP_SCANCODE_KEY_S,
"VK_KEY_S" },
96 { RDP_SCANCODE_KEY_D,
"VK_KEY_D" },
97 { RDP_SCANCODE_KEY_F,
"VK_KEY_F" },
98 { RDP_SCANCODE_KEY_G,
"VK_KEY_G" },
99 { RDP_SCANCODE_KEY_H,
"VK_KEY_H" },
100 { RDP_SCANCODE_KEY_J,
"VK_KEY_J" },
101 { RDP_SCANCODE_KEY_K,
"VK_KEY_K" },
102 { RDP_SCANCODE_KEY_L,
"VK_KEY_L" },
103 { RDP_SCANCODE_OEM_1,
"VK_OEM_1 ';' on US" },
104 { RDP_SCANCODE_OEM_7,
"VK_OEM_7 on US" },
105 { RDP_SCANCODE_OEM_3,
"VK_OEM_3 Top left, '`' on US, JP DBE_SBCSCHAR" },
106 { RDP_SCANCODE_LSHIFT,
"VK_LSHIFT" },
107 { RDP_SCANCODE_OEM_5,
"VK_OEM_5 Next to Enter, '\' on US" },
108 { RDP_SCANCODE_KEY_Z,
"VK_KEY_Z" },
109 { RDP_SCANCODE_KEY_X,
"VK_KEY_X" },
110 { RDP_SCANCODE_KEY_C,
"VK_KEY_C" },
111 { RDP_SCANCODE_KEY_V,
"VK_KEY_V" },
112 { RDP_SCANCODE_KEY_B,
"VK_KEY_B" },
113 { RDP_SCANCODE_KEY_N,
"VK_KEY_N" },
114 { RDP_SCANCODE_KEY_M,
"VK_KEY_M" },
115 { RDP_SCANCODE_OEM_COMMA,
"VK_OEM_COMMA" },
116 { RDP_SCANCODE_OEM_PERIOD,
"VK_OEM_PERIOD" },
117 { RDP_SCANCODE_OEM_2,
"VK_OEM_2 '/' on US" },
118 { RDP_SCANCODE_RSHIFT,
"VK_RSHIFT" },
119 { RDP_SCANCODE_MULTIPLY,
"VK_MULTIPLY Numerical" },
120 { RDP_SCANCODE_LMENU,
"VK_LMENU Left 'Alt' key" },
121 { RDP_SCANCODE_SPACE,
"VK_SPACE" },
122 { RDP_SCANCODE_CAPSLOCK,
"VK_CAPITAL 'Caps Lock', JP DBE_ALPHANUMERIC" },
123 { RDP_SCANCODE_F1,
"VK_F1" },
124 { RDP_SCANCODE_F2,
"VK_F2" },
125 { RDP_SCANCODE_F3,
"VK_F3" },
126 { RDP_SCANCODE_F4,
"VK_F4" },
127 { RDP_SCANCODE_F5,
"VK_F5" },
128 { RDP_SCANCODE_F6,
"VK_F6" },
129 { RDP_SCANCODE_F7,
"VK_F7" },
130 { RDP_SCANCODE_F8,
"VK_F8" },
131 { RDP_SCANCODE_F9,
"VK_F9" },
132 { RDP_SCANCODE_F10,
"VK_F10" },
133 { RDP_SCANCODE_NUMLOCK,
"VK_NUMLOCK" },
134 { RDP_SCANCODE_SCROLLLOCK,
"VK_SCROLL 'Scroll Lock', JP OEM_SCROLL" },
135 { RDP_SCANCODE_NUMPAD7,
"VK_NUMPAD7" },
136 { RDP_SCANCODE_NUMPAD8,
"VK_NUMPAD8" },
137 { RDP_SCANCODE_NUMPAD9,
"VK_NUMPAD9" },
138 { RDP_SCANCODE_SUBTRACT,
"VK_SUBTRACT" },
139 { RDP_SCANCODE_NUMPAD4,
"VK_NUMPAD4" },
140 { RDP_SCANCODE_NUMPAD5,
"VK_NUMPAD5" },
141 { RDP_SCANCODE_NUMPAD6,
"VK_NUMPAD6" },
142 { RDP_SCANCODE_ADD,
"VK_ADD" },
143 { RDP_SCANCODE_NUMPAD1,
"VK_NUMPAD1" },
144 { RDP_SCANCODE_NUMPAD2,
"VK_NUMPAD2" },
145 { RDP_SCANCODE_NUMPAD3,
"VK_NUMPAD3" },
146 { RDP_SCANCODE_NUMPAD0,
"VK_NUMPAD0" },
147 { RDP_SCANCODE_DECIMAL,
"VK_DECIMAL Numerical, '.' on US" },
148 { RDP_SCANCODE_SYSREQ,
"Sys Req" },
149 { RDP_SCANCODE_OEM_102,
"VK_OEM_102 Lower left '\' on US" },
150 { RDP_SCANCODE_F11,
"VK_F11" },
151 { RDP_SCANCODE_F12,
"VK_F12" },
152 { RDP_SCANCODE_SLEEP,
"VK_SLEEP OEM_8 on FR (undocumented?)" },
153 { RDP_SCANCODE_ZOOM,
"VK_ZOOM (undocumented?)" },
154 { RDP_SCANCODE_HELP,
"VK_HELP (undocumented?)" },
155 { RDP_SCANCODE_F13,
"VK_F13" },
156 { RDP_SCANCODE_F14,
"VK_F14" },
157 { RDP_SCANCODE_F15,
"VK_F15" },
158 { RDP_SCANCODE_F16,
"VK_F16" },
159 { RDP_SCANCODE_F17,
"VK_F17" },
160 { RDP_SCANCODE_F18,
"VK_F18" },
161 { RDP_SCANCODE_F19,
"VK_F19" },
162 { RDP_SCANCODE_F20,
"VK_F20" },
163 { RDP_SCANCODE_F21,
"VK_F21" },
164 { RDP_SCANCODE_F22,
"VK_F22" },
165 { RDP_SCANCODE_F23,
"VK_F23" },
166 { RDP_SCANCODE_F24,
"VK_F24" },
167 { RDP_SCANCODE_HIRAGANA,
"JP DBE_HIRAGANA" },
168 { RDP_SCANCODE_HANJA_KANJI,
"VK_HANJA / VK_KANJI (undocumented?)" },
169 { RDP_SCANCODE_KANA_HANGUL,
"VK_KANA / VK_HANGUL (undocumented?)" },
170 { RDP_SCANCODE_ABNT_C1,
"VK_ABNT_C1 JP OEM_102" },
171 { RDP_SCANCODE_F24_JP,
"JP F24" },
172 { RDP_SCANCODE_CONVERT_JP,
"JP VK_CONVERT" },
173 { RDP_SCANCODE_NONCONVERT_JP,
"JP VK_NONCONVERT" },
174 { RDP_SCANCODE_TAB_JP,
"JP TAB" },
175 { RDP_SCANCODE_BACKSLASH_JP,
"JP OEM_5 ('\')" },
176 { RDP_SCANCODE_ABNT_C2,
"VK_ABNT_C2, JP" },
177 { RDP_SCANCODE_HANJA,
"KR VK_HANJA" },
178 { RDP_SCANCODE_HANGUL,
"KR VK_HANGUL" },
179 { RDP_SCANCODE_RETURN_KP,
"not RDP_SCANCODE_RETURN Numerical Enter" },
180 { RDP_SCANCODE_RCONTROL,
"VK_RCONTROL" },
181 { RDP_SCANCODE_DIVIDE,
"VK_DIVIDE Numerical" },
182 { RDP_SCANCODE_PRINTSCREEN,
"VK_EXECUTE/VK_PRINT/VK_SNAPSHOT Print Screen" },
183 { RDP_SCANCODE_RMENU,
"VK_RMENU Right 'Alt' / 'Alt Gr'" },
184 { RDP_SCANCODE_PAUSE,
"VK_PAUSE Pause / Break (Slightly special handling)" },
185 { RDP_SCANCODE_HOME,
"VK_HOME" },
186 { RDP_SCANCODE_UP,
"VK_UP" },
187 { RDP_SCANCODE_PRIOR,
"VK_PRIOR 'Page Up'" },
188 { RDP_SCANCODE_LEFT,
"VK_LEFT" },
189 { RDP_SCANCODE_RIGHT,
"VK_RIGHT" },
190 { RDP_SCANCODE_END,
"VK_END" },
191 { RDP_SCANCODE_DOWN,
"VK_DOWN" },
192 { RDP_SCANCODE_NEXT,
"VK_NEXT 'Page Down'" },
193 { RDP_SCANCODE_INSERT,
"VK_INSERT" },
194 { RDP_SCANCODE_DELETE,
"VK_DELETE" },
195 { RDP_SCANCODE_NULL,
"<00>" },
196 { RDP_SCANCODE_HELP2,
"Help - documented, different from VK_HELP" },
197 { RDP_SCANCODE_LWIN,
"VK_LWIN" },
198 { RDP_SCANCODE_RWIN,
"VK_RWIN" },
199 { RDP_SCANCODE_APPS,
"VK_APPS Application" },
200 { RDP_SCANCODE_POWER_JP,
"JP POWER" },
201 { RDP_SCANCODE_SLEEP_JP,
"JP SLEEP" },
202 { RDP_SCANCODE_NUMLOCK_EXTENDED,
"should be RDP_SCANCODE_NUMLOCK" },
203 { RDP_SCANCODE_RSHIFT_EXTENDED,
"should be RDP_SCANCODE_RSHIFT" },
204 { RDP_SCANCODE_VOLUME_MUTE,
"VK_VOLUME_MUTE" },
205 { RDP_SCANCODE_VOLUME_DOWN,
"VK_VOLUME_DOWN" },
206 { RDP_SCANCODE_VOLUME_UP,
"VK_VOLUME_UP" },
207 { RDP_SCANCODE_MEDIA_NEXT_TRACK,
"VK_MEDIA_NEXT_TRACK" },
208 { RDP_SCANCODE_MEDIA_PREV_TRACK,
"VK_MEDIA_PREV_TRACK" },
209 { RDP_SCANCODE_MEDIA_STOP,
"VK_MEDIA_MEDIA_STOP" },
210 { RDP_SCANCODE_MEDIA_PLAY_PAUSE,
"VK_MEDIA_MEDIA_PLAY_PAUSE" },
211 { RDP_SCANCODE_BROWSER_BACK,
"VK_BROWSER_BACK" },
212 { RDP_SCANCODE_BROWSER_FORWARD,
"VK_BROWSER_FORWARD" },
213 { RDP_SCANCODE_BROWSER_REFRESH,
"VK_BROWSER_REFRESH" },
214 { RDP_SCANCODE_BROWSER_STOP,
"VK_BROWSER_STOP" },
215 { RDP_SCANCODE_BROWSER_SEARCH,
"VK_BROWSER_SEARCH" },
216 { RDP_SCANCODE_BROWSER_FAVORITES,
"VK_BROWSER_FAVORITES" },
217 { RDP_SCANCODE_BROWSER_HOME,
"VK_BROWSER_HOME" },
218 { RDP_SCANCODE_LAUNCH_MAIL,
"VK_LAUNCH_MAIL" },
219 { RDP_SCANCODE_LAUNCH_MEDIA_SELECT,
"VK_LAUNCH_MEDIA_SELECT" },
220 { RDP_SCANCODE_LAUNCH_APP1,
"VK_LAUNCH_APP1" },
221 { RDP_SCANCODE_LAUNCH_APP2,
"VK_LAUNCH_APP2" },
224 static int freerdp_detect_keyboard(DWORD* keyboardLayoutId)
227 CHAR name[KL_NAMELENGTH + 1] = { 0 };
228 if (GetKeyboardLayoutNameA(name))
233 rc = strtoul(name, NULL, 16);
235 *keyboardLayoutId = rc;
238 if (*keyboardLayoutId == 0)
239 *keyboardLayoutId = ((DWORD)GetKeyboardLayout(0) >> 16) & 0x0000FFFF;
242 #if defined(__MACOSX__)
243 if (*keyboardLayoutId == 0)
244 freerdp_detect_keyboard_layout_from_cf(keyboardLayoutId);
248 if (*keyboardLayoutId == 0)
249 freerdp_detect_keyboard_layout_from_xkb(keyboardLayoutId);
252 if (*keyboardLayoutId == 0)
253 freerdp_detect_keyboard_layout_from_system_locale(keyboardLayoutId);
255 if (*keyboardLayoutId == 0)
256 *keyboardLayoutId = ENGLISH_UNITED_STATES;
261 static int freerdp_keyboard_init_apple(
const DWORD* keyboardLayoutId,
262 DWORD* x11_keycode_to_rdp_scancode,
size_t count)
264 WINPR_ASSERT(x11_keycode_to_rdp_scancode);
265 WINPR_ASSERT(keyboardLayoutId);
266 WINPR_ASSERT(count <= UINT32_MAX);
267 for (
size_t keycode = 8; keycode < count; keycode++)
270 GetVirtualKeyCodeFromKeycode((UINT32)keycode - 8u, WINPR_KEYCODE_TYPE_APPLE);
271 x11_keycode_to_rdp_scancode[keycode] =
272 GetVirtualScanCodeFromVirtualKeyCode(vkcode, WINPR_KBD_TYPE_IBM_ENHANCED);
275 maptype = WINPR_KEYCODE_TYPE_APPLE;
279 static int freerdp_keyboard_init_x11_evdev(
const DWORD* keyboardLayoutId,
280 DWORD* x11_keycode_to_rdp_scancode,
size_t count)
282 WINPR_ASSERT(keyboardLayoutId);
283 WINPR_ASSERT(x11_keycode_to_rdp_scancode);
284 WINPR_ASSERT(count <= UINT32_MAX);
286 for (
size_t keycode = 0; keycode < count; keycode++)
289 GetVirtualKeyCodeFromKeycode((UINT32)keycode, WINPR_KEYCODE_TYPE_EVDEV);
290 x11_keycode_to_rdp_scancode[keycode] =
291 GetVirtualScanCodeFromVirtualKeyCode(vkcode, WINPR_KBD_TYPE_IBM_ENHANCED);
293 maptype = WINPR_KEYCODE_TYPE_EVDEV;
298 DWORD freerdp_keyboard_init(DWORD keyboardLayoutId)
302 #if defined(__APPLE__)
304 status = freerdp_keyboard_init_apple(&keyboardLayoutId, X11_KEYCODE_TO_VIRTUAL_SCANCODE,
305 ARRAYSIZE(X11_KEYCODE_TO_VIRTUAL_SCANCODE));
308 #if defined(WITH_X11) || defined(WITH_WAYLAND)
313 status = freerdp_keyboard_init_xkbfile(&keyboardLayoutId, X11_KEYCODE_TO_VIRTUAL_SCANCODE,
314 ARRAYSIZE(X11_KEYCODE_TO_VIRTUAL_SCANCODE));
316 maptype = WINPR_KEYCODE_TYPE_XKB;
321 status = freerdp_keyboard_init_x11_evdev(&keyboardLayoutId, X11_KEYCODE_TO_VIRTUAL_SCANCODE,
322 ARRAYSIZE(X11_KEYCODE_TO_VIRTUAL_SCANCODE));
327 WLog_DBG(TAG,
"Platform keyboard detection failed, trying autodetection");
329 freerdp_detect_keyboard(&keyboardLayoutId);
331 ZeroMemory(VIRTUAL_SCANCODE_TO_X11_KEYCODE,
sizeof(VIRTUAL_SCANCODE_TO_X11_KEYCODE));
333 WINPR_STATIC_ASSERT(ARRAYSIZE(VIRTUAL_SCANCODE_TO_X11_KEYCODE) <= UINT32_MAX);
334 for (
size_t keycode = 0; keycode < ARRAYSIZE(VIRTUAL_SCANCODE_TO_X11_KEYCODE); keycode++)
336 const DWORD x11 = X11_KEYCODE_TO_VIRTUAL_SCANCODE[keycode];
337 const DWORD sc = RDP_SCANCODE_CODE(x11);
338 const BOOL ex = RDP_SCANCODE_EXTENDED(x11);
339 VIRTUAL_SCANCODE_TO_X11_KEYCODE[sc][ex ? 1 : 0] = (UINT32)keycode;
342 return keyboardLayoutId;
345 DWORD freerdp_keyboard_init_ex(DWORD keyboardLayoutId,
const char* keyboardRemappingList)
347 DWORD res = freerdp_keyboard_init(keyboardLayoutId);
349 memset(REMAPPING_TABLE, 0,
sizeof(REMAPPING_TABLE));
350 if (keyboardRemappingList)
352 char* copy = _strdup(keyboardRemappingList);
353 char* context = NULL;
357 token = strtok_s(copy,
",", &context);
362 if (!freerdp_extract_key_value(token, &key, &value))
364 if (key >= ARRAYSIZE(REMAPPING_TABLE))
366 REMAPPING_TABLE[key] = value;
367 token = strtok_s(NULL,
",", &context);
375 DWORD freerdp_keyboard_get_rdp_scancode_from_x11_keycode(DWORD keycode)
377 if (keycode >= ARRAYSIZE(X11_KEYCODE_TO_VIRTUAL_SCANCODE))
379 WLog_ERR(TAG,
"KeyCode %" PRIu32
" exceeds allowed value range [0,%" PRIuz
"]", keycode,
380 ARRAYSIZE(X11_KEYCODE_TO_VIRTUAL_SCANCODE));
384 const DWORD scancode = X11_KEYCODE_TO_VIRTUAL_SCANCODE[keycode];
385 if (scancode >= ARRAYSIZE(REMAPPING_TABLE))
387 WLog_ERR(TAG,
"ScanCode %" PRIu32
" exceeds allowed value range [0,%" PRIuz
"]", scancode,
388 ARRAYSIZE(REMAPPING_TABLE));
392 const DWORD remapped = REMAPPING_TABLE[scancode];
393 #if defined(WITH_DEBUG_KBD)
394 const BOOL ex = RDP_SCANCODE_EXTENDED(scancode);
395 const DWORD sc = RDP_SCANCODE_CODE(scancode);
398 DEBUG_KBD(
"x11 keycode: %02" PRIX32
" -> rdp code: [%04" PRIx16
"] %02" PRIX8
"%s", keycode,
399 scancode, sc, ex ?
" extended" :
"");
403 #if defined(WITH_DEBUG_KBD)
404 const DWORD rsc = RDP_SCANCODE_CODE(remapped);
405 const BOOL rex = RDP_SCANCODE_EXTENDED(remapped);
408 DEBUG_KBD(
"remapped scancode: [%04" PRIx16
"] %02" PRIX8
"[%s] -> [%04" PRIx16
"] %02" PRIX8
410 scancode, sc, ex ?
" extended" :
"", remapped, rsc, rex ?
" extended" :
"");
416 DWORD freerdp_keyboard_get_x11_keycode_from_rdp_scancode(DWORD scancode, BOOL extended)
418 if (scancode >= ARRAYSIZE(VIRTUAL_SCANCODE_TO_X11_KEYCODE))
420 WLog_ERR(TAG,
"ScanCode %" PRIu32
" exceeds allowed value range [0,%" PRIuz
"]", scancode,
421 ARRAYSIZE(VIRTUAL_SCANCODE_TO_X11_KEYCODE));
425 const DWORD* x11 = VIRTUAL_SCANCODE_TO_X11_KEYCODE[scancode];
434 const char* freerdp_keyboard_scancode_name(DWORD scancode)
436 for (
size_t x = 0; x < ARRAYSIZE(RDP_SCANCODE_MAP); x++)
438 const struct scancode_map_entry* entry = &RDP_SCANCODE_MAP[x];
439 if (entry->scancode == scancode)