diff --git a/client/Wayland/wlfreerdp.c b/client/Wayland/wlfreerdp.c index 329d12009..8c9658131 100644 --- a/client/Wayland/wlfreerdp.c +++ b/client/Wayland/wlfreerdp.c @@ -270,7 +270,8 @@ static BOOL wl_post_connect(freerdp* instance) instance->update->BeginPaint = wl_begin_paint; instance->update->EndPaint = wl_end_paint; instance->update->DesktopResize = wl_resize_display; - freerdp_keyboard_init(instance->context->settings->KeyboardLayout); + freerdp_keyboard_init_ex(instance->context->settings->KeyboardLayout, + instance->context->settings->KeyboardRemappingList); if (!(context->disp = wlf_disp_new(context))) return FALSE; diff --git a/client/X11/xf_keyboard.c b/client/X11/xf_keyboard.c index 794acea0c..665804670 100644 --- a/client/X11/xf_keyboard.c +++ b/client/X11/xf_keyboard.c @@ -107,7 +107,8 @@ BOOL xf_keyboard_init(xfContext* xfc) { xf_keyboard_clear(xfc); xfc->KeyboardLayout = xfc->context.settings->KeyboardLayout; - xfc->KeyboardLayout = freerdp_keyboard_init(xfc->KeyboardLayout); + xfc->KeyboardLayout = + freerdp_keyboard_init_ex(xfc->KeyboardLayout, xfc->context.settings->KeyboardRemappingList); xfc->context.settings->KeyboardLayout = xfc->KeyboardLayout; if (xfc->modifierMap) diff --git a/client/common/cmdline.c b/client/common/cmdline.c index d4617023e..b1a394839 100644 --- a/client/common/cmdline.c +++ b/client/common/cmdline.c @@ -1989,6 +1989,11 @@ int freerdp_client_settings_parse_command_line_arguments(rdpSettings* settings, settings->KeyboardLayout = (UINT32)val; } + CommandLineSwitchCase(arg, "kbd-remap") + { + if (!copy_value(arg->Value, &settings->KeyboardRemappingList)) + return COMMAND_LINE_ERROR_MEMORY; + } CommandLineSwitchCase(arg, "kbd-lang") { LONGLONG val; diff --git a/client/common/cmdline.h b/client/common/cmdline.h index 3ae8af1f4..eec0735de 100644 --- a/client/common/cmdline.h +++ b/client/common/cmdline.h @@ -206,6 +206,9 @@ static const COMMAND_LINE_ARGUMENT_A args[] = { "List keyboard layouts" }, { "kbd-lang-list", COMMAND_LINE_VALUE_OPTIONAL | COMMAND_LINE_PRINT, NULL, NULL, NULL, -1, NULL, "List keyboard languages" }, + { "kbd-remap", COMMAND_LINE_VALUE_REQUIRED, + "List of =,... pairs to remap scancodes", NULL, NULL, -1, NULL, + "Keyboard scancode remapping" }, { "kbd-subtype", COMMAND_LINE_VALUE_REQUIRED, "", NULL, NULL, -1, NULL, "Keyboard subtype" }, { "kbd-type", COMMAND_LINE_VALUE_REQUIRED, "", NULL, NULL, -1, NULL, "Keyboard type" }, diff --git a/include/freerdp/locale/keyboard.h b/include/freerdp/locale/keyboard.h index 3f480462d..ed2e6be6f 100644 --- a/include/freerdp/locale/keyboard.h +++ b/include/freerdp/locale/keyboard.h @@ -220,6 +220,8 @@ extern "C" #endif FREERDP_API DWORD freerdp_keyboard_init(DWORD keyboardLayoutId); + FREERDP_API DWORD freerdp_keyboard_init_ex(DWORD keyboardLayoutId, + const char* keyboardRemappingList); FREERDP_API RDP_KEYBOARD_LAYOUT* freerdp_keyboard_get_layouts(DWORD types); FREERDP_API void freerdp_keyboard_layouts_free(RDP_KEYBOARD_LAYOUT* layouts); FREERDP_API const char* freerdp_keyboard_get_layout_name_from_id(DWORD keyboardLayoutId); diff --git a/include/freerdp/settings.h b/include/freerdp/settings.h index 99bc0d2dd..d9f68deac 100644 --- a/include/freerdp/settings.h +++ b/include/freerdp/settings.h @@ -799,6 +799,7 @@ typedef struct _RDPDR_PARALLEL RDPDR_PARALLEL; #define FreeRDP_BitmapCacheV2CellInfo (2502) #define FreeRDP_ColorPointerFlag (2560) #define FreeRDP_PointerCacheSize (2561) +#define FreeRDP_KeyboardRemappingList (2622) #define FreeRDP_KeyboardCodePage (2623) #define FreeRDP_KeyboardLayout (2624) #define FreeRDP_KeyboardType (2625) @@ -1332,9 +1333,10 @@ struct rdp_settings /* Pointer Capabilities */ ALIGN64 BOOL ColorPointerFlag; /* 2560 */ ALIGN64 UINT32 PointerCacheSize; /* 2561 */ - UINT64 padding2624[2623 - 2562]; /* 2562 */ + UINT64 padding2624[2622 - 2562]; /* 2562 */ /* Input Capabilities */ + ALIGN64 char* KeyboardRemappingList; /* 2622 */ ALIGN64 UINT32 KeyboardCodePage; /* 2623 */ ALIGN64 UINT32 KeyboardLayout; /* 2624 */ ALIGN64 UINT32 KeyboardType; /* 2625 */ diff --git a/libfreerdp/common/settings_getters.c b/libfreerdp/common/settings_getters.c index 2e49b8923..6842ae8ed 100644 --- a/libfreerdp/common/settings_getters.c +++ b/libfreerdp/common/settings_getters.c @@ -2247,6 +2247,9 @@ const char* freerdp_settings_get_string(const rdpSettings* settings, size_t id) case FreeRDP_KerberosRealm: return settings->KerberosRealm; + case FreeRDP_KeyboardRemappingList: + return settings->KeyboardRemappingList; + case FreeRDP_NtlmSamFile: return settings->NtlmSamFile; @@ -2551,6 +2554,12 @@ BOOL freerdp_settings_set_string_(rdpSettings* settings, size_t id, const char* settings->KerberosRealm = (val ? _strdup(val) : NULL); return (!val || settings->KerberosRealm != NULL); + case FreeRDP_KeyboardRemappingList: + if (cleanup) + free(settings->KeyboardRemappingList); + settings->KeyboardRemappingList = (val ? _strdup(val) : NULL); + return (!val || settings->KeyboardRemappingList != NULL); + case FreeRDP_NtlmSamFile: if (cleanup) free(settings->NtlmSamFile); diff --git a/libfreerdp/common/settings_str.c b/libfreerdp/common/settings_str.c index 699c94b2d..caa2b5084 100644 --- a/libfreerdp/common/settings_str.c +++ b/libfreerdp/common/settings_str.c @@ -324,6 +324,7 @@ static const struct settings_str_entry settings_map[] = { { FreeRDP_ImeFileName, 7, "FreeRDP_ImeFileName" }, { FreeRDP_KerberosKdc, 7, "FreeRDP_KerberosKdc" }, { FreeRDP_KerberosRealm, 7, "FreeRDP_KerberosRealm" }, + { FreeRDP_KeyboardRemappingList, 7, "FreeRDP_KeyboardRemappingList" }, { FreeRDP_NtlmSamFile, 7, "FreeRDP_NtlmSamFile" }, { FreeRDP_Password, 7, "FreeRDP_Password" }, { FreeRDP_PasswordHash, 7, "FreeRDP_PasswordHash" }, diff --git a/libfreerdp/core/test/settings_property_lists.h b/libfreerdp/core/test/settings_property_lists.h index c44018e47..77ba2379e 100644 --- a/libfreerdp/core/test/settings_property_lists.h +++ b/libfreerdp/core/test/settings_property_lists.h @@ -333,6 +333,7 @@ static const size_t string_list_indices[] = { FreeRDP_ImeFileName, FreeRDP_KerberosKdc, FreeRDP_KerberosRealm, + FreeRDP_KeyboardRemappingList, FreeRDP_NtlmSamFile, FreeRDP_Password, FreeRDP_PasswordHash, diff --git a/libfreerdp/locale/keyboard.c b/libfreerdp/locale/keyboard.c index de202d11a..cd2f69a2d 100644 --- a/libfreerdp/locale/keyboard.c +++ b/libfreerdp/locale/keyboard.c @@ -42,8 +42,9 @@ #endif -DWORD VIRTUAL_SCANCODE_TO_X11_KEYCODE[256][2]; -DWORD X11_KEYCODE_TO_VIRTUAL_SCANCODE[256]; +static DWORD VIRTUAL_SCANCODE_TO_X11_KEYCODE[256][2] = { 0 }; +static DWORD X11_KEYCODE_TO_VIRTUAL_SCANCODE[256] = { 0 }; +static DWORD REMAPPING_TABLE[0x10000] = { 0 }; int freerdp_detect_keyboard(DWORD* keyboardLayoutId) { @@ -138,13 +139,60 @@ DWORD freerdp_keyboard_init(DWORD keyboardLayoutId) return keyboardLayoutId; } +DWORD freerdp_keyboard_init_ex(DWORD keyboardLayoutId, const char* keyboardRemappingList) +{ + DWORD rc = freerdp_keyboard_init(keyboardLayoutId); + + memset(REMAPPING_TABLE, 0, sizeof(REMAPPING_TABLE)); + if (keyboardRemappingList) + { + char* copy = _strdup(keyboardRemappingList); + char* context = NULL; + char* token; + if (!copy) + goto fail; + token = strtok_s(copy, ",", &context); + while (token) + { + DWORD key, value; + int rc = sscanf(token, "%" PRIu32 "=%" PRIu32, &key, &value); + if (rc != 2) + rc = sscanf(token, "%" PRIx32 "=%" PRIx32 "", &key, &value); + if (rc != 2) + rc = sscanf(token, "%" PRIu32 "=%" PRIx32, &key, &value); + if (rc != 2) + rc = sscanf(token, "%" PRIx32 "=%" PRIu32, &key, &value); + if (rc != 2) + goto fail; + if (key >= ARRAYSIZE(REMAPPING_TABLE)) + goto fail; + REMAPPING_TABLE[key] = value; + token = strtok_s(NULL, ",", &context); + } + fail: + free(copy); + } + return rc; +} + DWORD freerdp_keyboard_get_rdp_scancode_from_x11_keycode(DWORD keycode) { - DEBUG_KBD("x11 keycode: %02" PRIX32 " -> rdp code: %02" PRIX8 "%s", keycode, - RDP_SCANCODE_CODE(X11_KEYCODE_TO_VIRTUAL_SCANCODE[keycode]), - RDP_SCANCODE_EXTENDED(X11_KEYCODE_TO_VIRTUAL_SCANCODE[keycode]) ? " extended" : ""); + const DWORD scancode = X11_KEYCODE_TO_VIRTUAL_SCANCODE[keycode]; + const DWORD remapped = REMAPPING_TABLE[scancode]; + DEBUG_KBD("x11 keycode: %02" PRIX32 " -> rdp code: [%04" PRIx16 "] %02" PRIX8 "%s", keycode, + scancode, RDP_SCANCODE_CODE(scancode), + RDP_SCANCODE_EXTENDED(scancode) ? " extended" : ""); - return X11_KEYCODE_TO_VIRTUAL_SCANCODE[keycode]; + if (remapped != 0) + { + DEBUG_KBD("remapped scancode: [%04" PRIx16 "] %02" PRIX8 "[%s] -> [%04" PRIx16 "] %02" PRIX8 + "[%s]", + scancode, RDP_SCANCODE_CODE(scancode), + RDP_SCANCODE_EXTENDED(scancode) ? " extended" : "", remapped, + RDP_SCANCODE_CODE(remapped), RDP_SCANCODE_EXTENDED(remapped) ? " extended" : ""); + return remapped; + } + return scancode; } DWORD freerdp_keyboard_get_x11_keycode_from_rdp_scancode(DWORD scancode, BOOL extended)