20#include <freerdp/config.h>
26#include <winpr/assert.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>
38#if !defined(WITHOUT_FREERDP_3x_DEPRECATED)
39#define TAG FREERDP_TAG("locale.keyboard")
41#if defined(__MACOSX__)
42#include "keyboard_apple.h"
46#include "keyboard_x11.h"
49#include "keyboard_xkbfile.h"
55#if !defined(WITHOUT_FREERDP_3x_DEPRECATED)
56static WINPR_KEYCODE_TYPE maptype = WINPR_KEYCODE_TYPE_NONE;
57static DWORD VIRTUAL_SCANCODE_TO_X11_KEYCODE[256][2] = { 0 };
58static DWORD X11_KEYCODE_TO_VIRTUAL_SCANCODE[256] = { 0 };
59static DWORD REMAPPING_TABLE[0x10000] = { 0 };
67struct scancode_map_entry
73static const struct scancode_map_entry RDP_SCANCODE_MAP[] = {
74 { RDP_SCANCODE_ESCAPE,
"VK_ESCAPE" },
75 { RDP_SCANCODE_KEY_1,
"VK_KEY_1" },
76 { RDP_SCANCODE_KEY_2,
"VK_KEY_2" },
77 { RDP_SCANCODE_KEY_3,
"VK_KEY_3" },
78 { RDP_SCANCODE_KEY_4,
"VK_KEY_4" },
79 { RDP_SCANCODE_KEY_5,
"VK_KEY_5" },
80 { RDP_SCANCODE_KEY_6,
"VK_KEY_6" },
81 { RDP_SCANCODE_KEY_7,
"VK_KEY_7" },
82 { RDP_SCANCODE_KEY_8,
"VK_KEY_8" },
83 { RDP_SCANCODE_KEY_9,
"VK_KEY_9" },
84 { RDP_SCANCODE_KEY_0,
"VK_KEY_0" },
85 { RDP_SCANCODE_OEM_MINUS,
"VK_OEM_MINUS" },
86 { RDP_SCANCODE_OEM_PLUS,
"VK_OEM_PLUS" },
87 { RDP_SCANCODE_BACKSPACE,
"VK_BACK Backspace" },
88 { RDP_SCANCODE_TAB,
"VK_TAB" },
89 { RDP_SCANCODE_KEY_Q,
"VK_KEY_Q" },
90 { RDP_SCANCODE_KEY_W,
"VK_KEY_W" },
91 { RDP_SCANCODE_KEY_E,
"VK_KEY_E" },
92 { RDP_SCANCODE_KEY_R,
"VK_KEY_R" },
93 { RDP_SCANCODE_KEY_T,
"VK_KEY_T" },
94 { RDP_SCANCODE_KEY_Y,
"VK_KEY_Y" },
95 { RDP_SCANCODE_KEY_U,
"VK_KEY_U" },
96 { RDP_SCANCODE_KEY_I,
"VK_KEY_I" },
97 { RDP_SCANCODE_KEY_O,
"VK_KEY_O" },
98 { RDP_SCANCODE_KEY_P,
"VK_KEY_P" },
99 { RDP_SCANCODE_OEM_4,
"VK_OEM_4 '[' on US" },
100 { RDP_SCANCODE_OEM_6,
"VK_OEM_6 ']' on US" },
101 { RDP_SCANCODE_RETURN,
"VK_RETURN Normal Enter" },
102 { RDP_SCANCODE_LCONTROL,
"VK_LCONTROL" },
103 { RDP_SCANCODE_KEY_A,
"VK_KEY_A" },
104 { RDP_SCANCODE_KEY_S,
"VK_KEY_S" },
105 { RDP_SCANCODE_KEY_D,
"VK_KEY_D" },
106 { RDP_SCANCODE_KEY_F,
"VK_KEY_F" },
107 { RDP_SCANCODE_KEY_G,
"VK_KEY_G" },
108 { RDP_SCANCODE_KEY_H,
"VK_KEY_H" },
109 { RDP_SCANCODE_KEY_J,
"VK_KEY_J" },
110 { RDP_SCANCODE_KEY_K,
"VK_KEY_K" },
111 { RDP_SCANCODE_KEY_L,
"VK_KEY_L" },
112 { RDP_SCANCODE_OEM_1,
"VK_OEM_1 ';' on US" },
113 { RDP_SCANCODE_OEM_7,
"VK_OEM_7 on US" },
114 { RDP_SCANCODE_OEM_3,
"VK_OEM_3 Top left, '`' on US, JP DBE_SBCSCHAR" },
115 { RDP_SCANCODE_LSHIFT,
"VK_LSHIFT" },
116 { RDP_SCANCODE_OEM_5,
"VK_OEM_5 Next to Enter, '\' on US" },
117 { RDP_SCANCODE_KEY_Z,
"VK_KEY_Z" },
118 { RDP_SCANCODE_KEY_X,
"VK_KEY_X" },
119 { RDP_SCANCODE_KEY_C,
"VK_KEY_C" },
120 { RDP_SCANCODE_KEY_V,
"VK_KEY_V" },
121 { RDP_SCANCODE_KEY_B,
"VK_KEY_B" },
122 { RDP_SCANCODE_KEY_N,
"VK_KEY_N" },
123 { RDP_SCANCODE_KEY_M,
"VK_KEY_M" },
124 { RDP_SCANCODE_OEM_COMMA,
"VK_OEM_COMMA" },
125 { RDP_SCANCODE_OEM_PERIOD,
"VK_OEM_PERIOD" },
126 { RDP_SCANCODE_OEM_2,
"VK_OEM_2 '/' on US" },
127 { RDP_SCANCODE_RSHIFT,
"VK_RSHIFT" },
128 { RDP_SCANCODE_MULTIPLY,
"VK_MULTIPLY Numerical" },
129 { RDP_SCANCODE_LMENU,
"VK_LMENU Left 'Alt' key" },
130 { RDP_SCANCODE_SPACE,
"VK_SPACE" },
131 { RDP_SCANCODE_CAPSLOCK,
"VK_CAPITAL 'Caps Lock', JP DBE_ALPHANUMERIC" },
132 { RDP_SCANCODE_F1,
"VK_F1" },
133 { RDP_SCANCODE_F2,
"VK_F2" },
134 { RDP_SCANCODE_F3,
"VK_F3" },
135 { RDP_SCANCODE_F4,
"VK_F4" },
136 { RDP_SCANCODE_F5,
"VK_F5" },
137 { RDP_SCANCODE_F6,
"VK_F6" },
138 { RDP_SCANCODE_F7,
"VK_F7" },
139 { RDP_SCANCODE_F8,
"VK_F8" },
140 { RDP_SCANCODE_F9,
"VK_F9" },
141 { RDP_SCANCODE_F10,
"VK_F10" },
142 { RDP_SCANCODE_NUMLOCK,
"VK_NUMLOCK" },
143 { RDP_SCANCODE_SCROLLLOCK,
"VK_SCROLL 'Scroll Lock', JP OEM_SCROLL" },
144 { RDP_SCANCODE_NUMPAD7,
"VK_NUMPAD7" },
145 { RDP_SCANCODE_NUMPAD8,
"VK_NUMPAD8" },
146 { RDP_SCANCODE_NUMPAD9,
"VK_NUMPAD9" },
147 { RDP_SCANCODE_SUBTRACT,
"VK_SUBTRACT" },
148 { RDP_SCANCODE_NUMPAD4,
"VK_NUMPAD4" },
149 { RDP_SCANCODE_NUMPAD5,
"VK_NUMPAD5" },
150 { RDP_SCANCODE_NUMPAD6,
"VK_NUMPAD6" },
151 { RDP_SCANCODE_ADD,
"VK_ADD" },
152 { RDP_SCANCODE_NUMPAD1,
"VK_NUMPAD1" },
153 { RDP_SCANCODE_NUMPAD2,
"VK_NUMPAD2" },
154 { RDP_SCANCODE_NUMPAD3,
"VK_NUMPAD3" },
155 { RDP_SCANCODE_NUMPAD0,
"VK_NUMPAD0" },
156 { RDP_SCANCODE_DECIMAL,
"VK_DECIMAL Numerical, '.' on US" },
157 { RDP_SCANCODE_SYSREQ,
"Sys Req" },
158 { RDP_SCANCODE_OEM_102,
"VK_OEM_102 Lower left '\' on US" },
159 { RDP_SCANCODE_F11,
"VK_F11" },
160 { RDP_SCANCODE_F12,
"VK_F12" },
161 { RDP_SCANCODE_SLEEP,
"VK_SLEEP OEM_8 on FR (undocumented?)" },
162 { RDP_SCANCODE_ZOOM,
"VK_ZOOM (undocumented?)" },
163 { RDP_SCANCODE_HELP,
"VK_HELP (undocumented?)" },
164 { RDP_SCANCODE_F13,
"VK_F13" },
165 { RDP_SCANCODE_F14,
"VK_F14" },
166 { RDP_SCANCODE_F15,
"VK_F15" },
167 { RDP_SCANCODE_F16,
"VK_F16" },
168 { RDP_SCANCODE_F17,
"VK_F17" },
169 { RDP_SCANCODE_F18,
"VK_F18" },
170 { RDP_SCANCODE_F19,
"VK_F19" },
171 { RDP_SCANCODE_F20,
"VK_F20" },
172 { RDP_SCANCODE_F21,
"VK_F21" },
173 { RDP_SCANCODE_F22,
"VK_F22" },
174 { RDP_SCANCODE_F23,
"VK_F23" },
175 { RDP_SCANCODE_F24,
"VK_F24" },
176 { RDP_SCANCODE_HIRAGANA,
"JP DBE_HIRAGANA" },
177 { RDP_SCANCODE_HANJA_KANJI,
"VK_HANJA / VK_KANJI (undocumented?)" },
178 { RDP_SCANCODE_KANA_HANGUL,
"VK_KANA / VK_HANGUL (undocumented?)" },
179 { RDP_SCANCODE_ABNT_C1,
"VK_ABNT_C1 JP OEM_102" },
180 { RDP_SCANCODE_F24_JP,
"JP F24" },
181 { RDP_SCANCODE_CONVERT_JP,
"JP VK_CONVERT" },
182 { RDP_SCANCODE_NONCONVERT_JP,
"JP VK_NONCONVERT" },
183 { RDP_SCANCODE_TAB_JP,
"JP TAB" },
184 { RDP_SCANCODE_BACKSLASH_JP,
"JP OEM_5 ('\')" },
185 { RDP_SCANCODE_ABNT_C2,
"VK_ABNT_C2, JP" },
186 { RDP_SCANCODE_HANJA,
"KR VK_HANJA" },
187 { RDP_SCANCODE_HANGUL,
"KR VK_HANGUL" },
188 { RDP_SCANCODE_RETURN_KP,
"not RDP_SCANCODE_RETURN Numerical Enter" },
189 { RDP_SCANCODE_RCONTROL,
"VK_RCONTROL" },
190 { RDP_SCANCODE_DIVIDE,
"VK_DIVIDE Numerical" },
191 { RDP_SCANCODE_PRINTSCREEN,
"VK_EXECUTE/VK_PRINT/VK_SNAPSHOT Print Screen" },
192 { RDP_SCANCODE_RMENU,
"VK_RMENU Right 'Alt' / 'Alt Gr'" },
193 { RDP_SCANCODE_PAUSE,
"VK_PAUSE Pause / Break (Slightly special handling)" },
194 { RDP_SCANCODE_HOME,
"VK_HOME" },
195 { RDP_SCANCODE_UP,
"VK_UP" },
196 { RDP_SCANCODE_PRIOR,
"VK_PRIOR 'Page Up'" },
197 { RDP_SCANCODE_LEFT,
"VK_LEFT" },
198 { RDP_SCANCODE_RIGHT,
"VK_RIGHT" },
199 { RDP_SCANCODE_END,
"VK_END" },
200 { RDP_SCANCODE_DOWN,
"VK_DOWN" },
201 { RDP_SCANCODE_NEXT,
"VK_NEXT 'Page Down'" },
202 { RDP_SCANCODE_INSERT,
"VK_INSERT" },
203 { RDP_SCANCODE_DELETE,
"VK_DELETE" },
204 { RDP_SCANCODE_NULL,
"<00>" },
205 { RDP_SCANCODE_HELP2,
"Help - documented, different from VK_HELP" },
206 { RDP_SCANCODE_LWIN,
"VK_LWIN" },
207 { RDP_SCANCODE_RWIN,
"VK_RWIN" },
208 { RDP_SCANCODE_APPS,
"VK_APPS Application" },
209 { RDP_SCANCODE_POWER_JP,
"JP POWER" },
210 { RDP_SCANCODE_SLEEP_JP,
"JP SLEEP" },
211 { RDP_SCANCODE_NUMLOCK_EXTENDED,
"should be RDP_SCANCODE_NUMLOCK" },
212 { RDP_SCANCODE_RSHIFT_EXTENDED,
"should be RDP_SCANCODE_RSHIFT" },
213 { RDP_SCANCODE_VOLUME_MUTE,
"VK_VOLUME_MUTE" },
214 { RDP_SCANCODE_VOLUME_DOWN,
"VK_VOLUME_DOWN" },
215 { RDP_SCANCODE_VOLUME_UP,
"VK_VOLUME_UP" },
216 { RDP_SCANCODE_MEDIA_NEXT_TRACK,
"VK_MEDIA_NEXT_TRACK" },
217 { RDP_SCANCODE_MEDIA_PREV_TRACK,
"VK_MEDIA_PREV_TRACK" },
218 { RDP_SCANCODE_MEDIA_STOP,
"VK_MEDIA_MEDIA_STOP" },
219 { RDP_SCANCODE_MEDIA_PLAY_PAUSE,
"VK_MEDIA_MEDIA_PLAY_PAUSE" },
220 { RDP_SCANCODE_BROWSER_BACK,
"VK_BROWSER_BACK" },
221 { RDP_SCANCODE_BROWSER_FORWARD,
"VK_BROWSER_FORWARD" },
222 { RDP_SCANCODE_BROWSER_REFRESH,
"VK_BROWSER_REFRESH" },
223 { RDP_SCANCODE_BROWSER_STOP,
"VK_BROWSER_STOP" },
224 { RDP_SCANCODE_BROWSER_SEARCH,
"VK_BROWSER_SEARCH" },
225 { RDP_SCANCODE_BROWSER_FAVORITES,
"VK_BROWSER_FAVORITES" },
226 { RDP_SCANCODE_BROWSER_HOME,
"VK_BROWSER_HOME" },
227 { RDP_SCANCODE_LAUNCH_MAIL,
"VK_LAUNCH_MAIL" },
228 { RDP_SCANCODE_LAUNCH_MEDIA_SELECT,
"VK_LAUNCH_MEDIA_SELECT" },
229 { RDP_SCANCODE_LAUNCH_APP1,
"VK_LAUNCH_APP1" },
230 { RDP_SCANCODE_LAUNCH_APP2,
"VK_LAUNCH_APP2" },
233#if !defined(WITHOUT_FREERDP_3x_DEPRECATED)
234static int freerdp_detect_keyboard(DWORD* keyboardLayoutId)
237 CHAR name[KL_NAMELENGTH + 1] = { 0 };
238 if (GetKeyboardLayoutNameA(name))
243 rc = strtoul(name, NULL, 16);
245 *keyboardLayoutId = rc;
248 if (*keyboardLayoutId == 0)
250 const HKL layout = GetKeyboardLayout(0);
251 const uint32_t masked = (uint32_t)(((uintptr_t)layout >> 16) & 0xFFFF);
252 *keyboardLayoutId = masked;
256#if defined(__MACOSX__)
257 if (*keyboardLayoutId == 0)
258 freerdp_detect_keyboard_layout_from_cf(keyboardLayoutId);
262 if (*keyboardLayoutId == 0)
263 freerdp_detect_keyboard_layout_from_xkb(keyboardLayoutId);
266 if (*keyboardLayoutId == 0)
267 freerdp_detect_keyboard_layout_from_system_locale(keyboardLayoutId);
269 if (*keyboardLayoutId == 0)
270 *keyboardLayoutId = ENGLISH_UNITED_STATES;
275#if defined(__APPLE__)
276static int freerdp_keyboard_init_apple(WINPR_ATTR_UNUSED
const DWORD* keyboardLayoutId,
277 DWORD* x11_keycode_to_rdp_scancode,
size_t count)
279 WINPR_ASSERT(x11_keycode_to_rdp_scancode);
280 WINPR_ASSERT(keyboardLayoutId);
281 WINPR_ASSERT(count <= UINT32_MAX);
282 for (
size_t keycode = 8; keycode < count; keycode++)
285 GetVirtualKeyCodeFromKeycode((UINT32)keycode - 8u, WINPR_KEYCODE_TYPE_APPLE);
286 x11_keycode_to_rdp_scancode[keycode] =
287 GetVirtualScanCodeFromVirtualKeyCode(vkcode, WINPR_KBD_TYPE_IBM_ENHANCED);
290 maptype = WINPR_KEYCODE_TYPE_APPLE;
295static int freerdp_keyboard_init_x11_evdev(WINPR_ATTR_UNUSED
const DWORD* keyboardLayoutId,
296 DWORD* x11_keycode_to_rdp_scancode,
size_t count)
298 WINPR_ASSERT(keyboardLayoutId);
299 WINPR_ASSERT(x11_keycode_to_rdp_scancode);
300 WINPR_ASSERT(count <= UINT32_MAX);
302 for (
size_t keycode = 0; keycode < count; keycode++)
305 GetVirtualKeyCodeFromKeycode((UINT32)keycode, WINPR_KEYCODE_TYPE_EVDEV);
306 x11_keycode_to_rdp_scancode[keycode] =
307 GetVirtualScanCodeFromVirtualKeyCode(vkcode, WINPR_KBD_TYPE_IBM_ENHANCED);
309 maptype = WINPR_KEYCODE_TYPE_EVDEV;
314DWORD freerdp_keyboard_init(DWORD keyboardLayoutId)
318#if defined(__APPLE__)
320 status = freerdp_keyboard_init_apple(&keyboardLayoutId, X11_KEYCODE_TO_VIRTUAL_SCANCODE,
321 ARRAYSIZE(X11_KEYCODE_TO_VIRTUAL_SCANCODE));
324#if defined(WITH_X11) || defined(WITH_WAYLAND)
329 status = freerdp_keyboard_init_xkbfile(&keyboardLayoutId, X11_KEYCODE_TO_VIRTUAL_SCANCODE,
330 ARRAYSIZE(X11_KEYCODE_TO_VIRTUAL_SCANCODE));
332 maptype = WINPR_KEYCODE_TYPE_XKB;
337 status = freerdp_keyboard_init_x11_evdev(&keyboardLayoutId, X11_KEYCODE_TO_VIRTUAL_SCANCODE,
338 ARRAYSIZE(X11_KEYCODE_TO_VIRTUAL_SCANCODE));
343 WLog_DBG(TAG,
"Platform keyboard detection failed, trying autodetection");
345 freerdp_detect_keyboard(&keyboardLayoutId);
347 ZeroMemory(VIRTUAL_SCANCODE_TO_X11_KEYCODE,
sizeof(VIRTUAL_SCANCODE_TO_X11_KEYCODE));
349 WINPR_STATIC_ASSERT(ARRAYSIZE(VIRTUAL_SCANCODE_TO_X11_KEYCODE) <= UINT32_MAX);
350 for (
size_t keycode = 0; keycode < ARRAYSIZE(VIRTUAL_SCANCODE_TO_X11_KEYCODE); keycode++)
352 const DWORD x11 = X11_KEYCODE_TO_VIRTUAL_SCANCODE[keycode];
353 const DWORD sc = RDP_SCANCODE_CODE(x11);
354 const BOOL ex = RDP_SCANCODE_EXTENDED(x11);
355 VIRTUAL_SCANCODE_TO_X11_KEYCODE[sc][ex ? 1 : 0] = (UINT32)keycode;
358 return keyboardLayoutId;
362FREERDP_REMAP_TABLE* freerdp_keyboard_remap_string_to_list(
const char* list)
364 const size_t remap_table_size = 0x10000;
366 FREERDP_REMAP_TABLE* remap_table = calloc(1,
sizeof(FREERDP_REMAP_TABLE));
370 for (
size_t x = 0; x < ARRAYSIZE(remap_table->table); x++)
371 remap_table->table[x] = (UINT32)x;
376 BOOL success = FALSE;
377 char* copy = _strdup(list);
381 char* context = NULL;
382 char* token = strtok_s(copy,
",", &context);
387 if (!freerdp_extract_key_value(token, &key, &value))
389 if (key >= remap_table_size)
391 remap_table->table[key] = value;
392 token = strtok_s(NULL,
",", &context);
408#if !defined(WITHOUT_FREERDP_3x_DEPRECATED)
409DWORD freerdp_keyboard_init_ex(DWORD keyboardLayoutId,
const char* keyboardRemappingList)
411 DWORD res = freerdp_keyboard_init(keyboardLayoutId);
413 memset(REMAPPING_TABLE, 0,
sizeof(REMAPPING_TABLE));
414 if (keyboardRemappingList)
416 char* copy = _strdup(keyboardRemappingList);
417 char* context = NULL;
421 token = strtok_s(copy,
",", &context);
426 if (!freerdp_extract_key_value(token, &key, &value))
428 if (key >= ARRAYSIZE(REMAPPING_TABLE))
430 REMAPPING_TABLE[key] = value;
431 token = strtok_s(NULL,
",", &context);
439DWORD freerdp_keyboard_get_rdp_scancode_from_x11_keycode(DWORD keycode)
441 if (keycode >= ARRAYSIZE(X11_KEYCODE_TO_VIRTUAL_SCANCODE))
443 WLog_ERR(TAG,
"KeyCode %" PRIu32
" exceeds allowed value range [0,%" PRIuz
"]", keycode,
444 ARRAYSIZE(X11_KEYCODE_TO_VIRTUAL_SCANCODE));
448 const DWORD scancode = X11_KEYCODE_TO_VIRTUAL_SCANCODE[keycode];
449 if (scancode >= ARRAYSIZE(REMAPPING_TABLE))
451 WLog_ERR(TAG,
"ScanCode %" PRIu32
" exceeds allowed value range [0,%" PRIuz
"]", scancode,
452 ARRAYSIZE(REMAPPING_TABLE));
456 const DWORD remapped = REMAPPING_TABLE[scancode];
457#if defined(WITH_DEBUG_KBD)
458 const BOOL ex = RDP_SCANCODE_EXTENDED(scancode);
459 const DWORD sc = RDP_SCANCODE_CODE(scancode);
462 DEBUG_KBD(
"x11 keycode: %02" PRIX32
" -> rdp code: [%04" PRIx16
"] %02" PRIX8
"%s", keycode,
463 scancode, sc, ex ?
" extended" :
"");
467#if defined(WITH_DEBUG_KBD)
468 const DWORD rsc = RDP_SCANCODE_CODE(remapped);
469 const BOOL rex = RDP_SCANCODE_EXTENDED(remapped);
472 DEBUG_KBD(
"remapped scancode: [%04" PRIx16
"] %02" PRIX8
"[%s] -> [%04" PRIx16
"] %02" PRIX8
474 scancode, sc, ex ?
" extended" :
"", remapped, rsc, rex ?
" extended" :
"");
480DWORD freerdp_keyboard_get_x11_keycode_from_rdp_scancode(DWORD scancode, BOOL extended)
482 if (scancode >= ARRAYSIZE(VIRTUAL_SCANCODE_TO_X11_KEYCODE))
484 WLog_ERR(TAG,
"ScanCode %" PRIu32
" exceeds allowed value range [0,%" PRIuz
"]", scancode,
485 ARRAYSIZE(VIRTUAL_SCANCODE_TO_X11_KEYCODE));
489 const DWORD* x11 = VIRTUAL_SCANCODE_TO_X11_KEYCODE[scancode];
499const char* freerdp_keyboard_scancode_name(DWORD scancode)
501 for (
size_t x = 0; x < ARRAYSIZE(RDP_SCANCODE_MAP); x++)
503 const struct scancode_map_entry* entry = &RDP_SCANCODE_MAP[x];
504 if (entry->scancode == scancode)
511DWORD freerdp_keyboard_remap_key(
const FREERDP_REMAP_TABLE* remap_table, DWORD rdpScanCode)
513 if (!remap_table || (ARRAYSIZE(remap_table->table) <= rdpScanCode))
516 return remap_table->table[rdpScanCode];
519void freerdp_keyboard_remap_free(FREERDP_REMAP_TABLE* table)