Milestone 5: deliver embedded RDP sessions and lifecycle hardening

This commit is contained in:
Keith Smith
2026-03-03 18:59:26 -07:00
parent 230a401386
commit 36006bd4aa
2941 changed files with 724359 additions and 77 deletions

View File

@@ -0,0 +1,90 @@
# FreeRDP: A Remote Desktop Protocol Implementation
# libfreerdp-locale cmake build script
#
# Copyright 2012 Marc-Andre Moreau <marcandre.moreau@gmail.com>
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
set(MODULE_NAME "freerdp-locale")
set(MODULE_PREFIX "FREERDP_LOCALE")
set(SRCS keyboard_layout.c keyboard.c locale.c liblocale.h)
if(NOT WITHOUT_FREERDP_3x_DEPRECATED)
set(X11_SRCS keyboard_x11.c keyboard_x11.h xkb_layout_ids.c xkb_layout_ids.h)
set(XKBFILE_SRCS keyboard_xkbfile.c keyboard_xkbfile.h)
endif()
set(SUN_SRCS keyboard_sun.c keyboard_sun.h)
set(APPLE_SRCS keyboard_apple.c keyboard_apple.h)
include(CMakeDependentOption)
cmake_dependent_option(WITH_KEYBOARD_LAYOUT_FROM_FILE "Use keyboard definitions from JSON file" OFF WITH_WINPR_JSON OFF)
if(WITH_KEYBOARD_LAYOUT_FROM_FILE)
freerdp_definition_add(FREERDP_RESOURCE_ROOT="${FREERDP_RESOURCE_ROOT}")
freerdp_definition_add(WITH_KEYBOARD_LAYOUT_FROM_FILE)
install(FILES KeyboardLayoutMap.json DESTINATION ${FREERDP_RESOURCE_ROOT})
endif()
if(CMAKE_SYSTEM_NAME MATCHES Solaris)
set(WITH_SUN true)
endif()
if(APPLE AND (NOT IOS))
list(APPEND SRCS ${APPLE_SRCS})
find_library(CARBON Carbon)
freerdp_library_add(${CARBON})
endif()
if(APPLE)
find_library(CORE_FOUNDATION CoreFoundation REQUIRED)
freerdp_library_add(${CORE_FOUNDATION})
endif()
if(WITH_X11 AND NOT WITHOUT_FREERDP_3x_DEPRECATED)
find_package(X11 REQUIRED)
freerdp_pc_add_requires_private("x11")
freerdp_definition_add(WITH_X11)
freerdp_include_directory_add(${X11_INCLUDE_DIR})
list(APPEND SRCS ${X11_SRCS})
freerdp_library_add(${X11_LIBRARIES})
if(WITH_SUN)
freerdp_definition_add(WITH_SUN)
list(APPEND SRCS ${SUN_SRCS})
endif()
if(X11_Xkbfile_FOUND AND (NOT APPLE))
freerdp_pc_add_requires_private("xkbfile")
freerdp_definition_add(WITH_XKBFILE)
freerdp_include_directory_add(${X11_Xkbfile_INCLUDE_PATH})
list(APPEND SRCS ${XKBFILE_SRCS})
freerdp_library_add(${X11_Xkbfile_LIB})
else()
list(APPEND SRCS ${X11_KEYMAP_SRCS})
endif()
endif()
if(WITH_WAYLAND AND NOT WITHOUT_FREERDP_3x_DEPRECATED)
freerdp_definition_add(WITH_WAYLAND)
endif()
freerdp_module_add(${SRCS})
if(BUILD_TESTING_INTERNAL OR BUILD_TESTING)
add_subdirectory(test)
endif()

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,524 @@
/**
* FreeRDP: A Remote Desktop Protocol Implementation
* Keyboard Localization
*
* Copyright 2009-2012 Marc-Andre Moreau <marcandre.moreau@gmail.com>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <freerdp/config.h>
#include <stdio.h>
#include <string.h>
#include <stdint.h>
#include <winpr/assert.h>
#include <winpr/crt.h>
#include <freerdp/utils/string.h>
#include <freerdp/types.h>
#include <freerdp/locale/keyboard.h>
#include <freerdp/locale/locale.h>
#include <freerdp/log.h>
#include "liblocale.h"
#if !defined(WITHOUT_FREERDP_3x_DEPRECATED)
#define TAG FREERDP_TAG("locale.keyboard")
#if defined(__MACOSX__)
#include "keyboard_apple.h"
#endif
#ifdef WITH_X11
#include "keyboard_x11.h"
#ifdef WITH_XKBFILE
#include "keyboard_xkbfile.h"
#endif
#endif
#endif
#if !defined(WITHOUT_FREERDP_3x_DEPRECATED)
static WINPR_KEYCODE_TYPE maptype = WINPR_KEYCODE_TYPE_NONE;
static DWORD VIRTUAL_SCANCODE_TO_X11_KEYCODE[256][2] = WINPR_C_ARRAY_INIT;
static DWORD X11_KEYCODE_TO_VIRTUAL_SCANCODE[256] = WINPR_C_ARRAY_INIT;
static DWORD REMAPPING_TABLE[0x10000] = WINPR_C_ARRAY_INIT;
#endif
struct rdp_remap_table
{
DWORD table[0x10000];
};
struct scancode_map_entry
{
DWORD scancode;
const char* name;
};
static const struct scancode_map_entry RDP_SCANCODE_MAP[] = {
{ RDP_SCANCODE_ESCAPE, "VK_ESCAPE" },
{ RDP_SCANCODE_KEY_1, "VK_KEY_1" },
{ RDP_SCANCODE_KEY_2, "VK_KEY_2" },
{ RDP_SCANCODE_KEY_3, "VK_KEY_3" },
{ RDP_SCANCODE_KEY_4, "VK_KEY_4" },
{ RDP_SCANCODE_KEY_5, "VK_KEY_5" },
{ RDP_SCANCODE_KEY_6, "VK_KEY_6" },
{ RDP_SCANCODE_KEY_7, "VK_KEY_7" },
{ RDP_SCANCODE_KEY_8, "VK_KEY_8" },
{ RDP_SCANCODE_KEY_9, "VK_KEY_9" },
{ RDP_SCANCODE_KEY_0, "VK_KEY_0" },
{ RDP_SCANCODE_OEM_MINUS, "VK_OEM_MINUS" },
{ RDP_SCANCODE_OEM_PLUS, "VK_OEM_PLUS" },
{ RDP_SCANCODE_BACKSPACE, "VK_BACK Backspace" },
{ RDP_SCANCODE_TAB, "VK_TAB" },
{ RDP_SCANCODE_KEY_Q, "VK_KEY_Q" },
{ RDP_SCANCODE_KEY_W, "VK_KEY_W" },
{ RDP_SCANCODE_KEY_E, "VK_KEY_E" },
{ RDP_SCANCODE_KEY_R, "VK_KEY_R" },
{ RDP_SCANCODE_KEY_T, "VK_KEY_T" },
{ RDP_SCANCODE_KEY_Y, "VK_KEY_Y" },
{ RDP_SCANCODE_KEY_U, "VK_KEY_U" },
{ RDP_SCANCODE_KEY_I, "VK_KEY_I" },
{ RDP_SCANCODE_KEY_O, "VK_KEY_O" },
{ RDP_SCANCODE_KEY_P, "VK_KEY_P" },
{ RDP_SCANCODE_OEM_4, "VK_OEM_4 '[' on US" },
{ RDP_SCANCODE_OEM_6, "VK_OEM_6 ']' on US" },
{ RDP_SCANCODE_RETURN, "VK_RETURN Normal Enter" },
{ RDP_SCANCODE_LCONTROL, "VK_LCONTROL" },
{ RDP_SCANCODE_KEY_A, "VK_KEY_A" },
{ RDP_SCANCODE_KEY_S, "VK_KEY_S" },
{ RDP_SCANCODE_KEY_D, "VK_KEY_D" },
{ RDP_SCANCODE_KEY_F, "VK_KEY_F" },
{ RDP_SCANCODE_KEY_G, "VK_KEY_G" },
{ RDP_SCANCODE_KEY_H, "VK_KEY_H" },
{ RDP_SCANCODE_KEY_J, "VK_KEY_J" },
{ RDP_SCANCODE_KEY_K, "VK_KEY_K" },
{ RDP_SCANCODE_KEY_L, "VK_KEY_L" },
{ RDP_SCANCODE_OEM_1, "VK_OEM_1 ';' on US" },
{ RDP_SCANCODE_OEM_7, "VK_OEM_7 on US" },
{ RDP_SCANCODE_OEM_3, "VK_OEM_3 Top left, '`' on US, JP DBE_SBCSCHAR" },
{ RDP_SCANCODE_LSHIFT, "VK_LSHIFT" },
{ RDP_SCANCODE_OEM_5, "VK_OEM_5 Next to Enter, '\' on US" },
{ RDP_SCANCODE_KEY_Z, "VK_KEY_Z" },
{ RDP_SCANCODE_KEY_X, "VK_KEY_X" },
{ RDP_SCANCODE_KEY_C, "VK_KEY_C" },
{ RDP_SCANCODE_KEY_V, "VK_KEY_V" },
{ RDP_SCANCODE_KEY_B, "VK_KEY_B" },
{ RDP_SCANCODE_KEY_N, "VK_KEY_N" },
{ RDP_SCANCODE_KEY_M, "VK_KEY_M" },
{ RDP_SCANCODE_OEM_COMMA, "VK_OEM_COMMA" },
{ RDP_SCANCODE_OEM_PERIOD, "VK_OEM_PERIOD" },
{ RDP_SCANCODE_OEM_2, "VK_OEM_2 '/' on US" },
{ RDP_SCANCODE_RSHIFT, "VK_RSHIFT" },
{ RDP_SCANCODE_MULTIPLY, "VK_MULTIPLY Numerical" },
{ RDP_SCANCODE_LMENU, "VK_LMENU Left 'Alt' key" },
{ RDP_SCANCODE_SPACE, "VK_SPACE" },
{ RDP_SCANCODE_CAPSLOCK, "VK_CAPITAL 'Caps Lock', JP DBE_ALPHANUMERIC" },
{ RDP_SCANCODE_F1, "VK_F1" },
{ RDP_SCANCODE_F2, "VK_F2" },
{ RDP_SCANCODE_F3, "VK_F3" },
{ RDP_SCANCODE_F4, "VK_F4" },
{ RDP_SCANCODE_F5, "VK_F5" },
{ RDP_SCANCODE_F6, "VK_F6" },
{ RDP_SCANCODE_F7, "VK_F7" },
{ RDP_SCANCODE_F8, "VK_F8" },
{ RDP_SCANCODE_F9, "VK_F9" },
{ RDP_SCANCODE_F10, "VK_F10" },
{ RDP_SCANCODE_NUMLOCK, "VK_NUMLOCK" },
{ RDP_SCANCODE_SCROLLLOCK, "VK_SCROLL 'Scroll Lock', JP OEM_SCROLL" },
{ RDP_SCANCODE_NUMPAD7, "VK_NUMPAD7" },
{ RDP_SCANCODE_NUMPAD8, "VK_NUMPAD8" },
{ RDP_SCANCODE_NUMPAD9, "VK_NUMPAD9" },
{ RDP_SCANCODE_SUBTRACT, "VK_SUBTRACT" },
{ RDP_SCANCODE_NUMPAD4, "VK_NUMPAD4" },
{ RDP_SCANCODE_NUMPAD5, "VK_NUMPAD5" },
{ RDP_SCANCODE_NUMPAD6, "VK_NUMPAD6" },
{ RDP_SCANCODE_ADD, "VK_ADD" },
{ RDP_SCANCODE_NUMPAD1, "VK_NUMPAD1" },
{ RDP_SCANCODE_NUMPAD2, "VK_NUMPAD2" },
{ RDP_SCANCODE_NUMPAD3, "VK_NUMPAD3" },
{ RDP_SCANCODE_NUMPAD0, "VK_NUMPAD0" },
{ RDP_SCANCODE_DECIMAL, "VK_DECIMAL Numerical, '.' on US" },
{ RDP_SCANCODE_SYSREQ, "Sys Req" },
{ RDP_SCANCODE_OEM_102, "VK_OEM_102 Lower left '\' on US" },
{ RDP_SCANCODE_F11, "VK_F11" },
{ RDP_SCANCODE_F12, "VK_F12" },
{ RDP_SCANCODE_SLEEP, "VK_SLEEP OEM_8 on FR (undocumented?)" },
{ RDP_SCANCODE_ZOOM, "VK_ZOOM (undocumented?)" },
{ RDP_SCANCODE_HELP, "VK_HELP (undocumented?)" },
{ RDP_SCANCODE_F13, "VK_F13" },
{ RDP_SCANCODE_F14, "VK_F14" },
{ RDP_SCANCODE_F15, "VK_F15" },
{ RDP_SCANCODE_F16, "VK_F16" },
{ RDP_SCANCODE_F17, "VK_F17" },
{ RDP_SCANCODE_F18, "VK_F18" },
{ RDP_SCANCODE_F19, "VK_F19" },
{ RDP_SCANCODE_F20, "VK_F20" },
{ RDP_SCANCODE_F21, "VK_F21" },
{ RDP_SCANCODE_F22, "VK_F22" },
{ RDP_SCANCODE_F23, "VK_F23" },
{ RDP_SCANCODE_F24, "VK_F24" },
{ RDP_SCANCODE_HIRAGANA, "JP DBE_HIRAGANA" },
{ RDP_SCANCODE_HANJA_KANJI, "VK_HANJA / VK_KANJI (undocumented?)" },
{ RDP_SCANCODE_KANA_HANGUL, "VK_KANA / VK_HANGUL (undocumented?)" },
{ RDP_SCANCODE_ABNT_C1, "VK_ABNT_C1 JP OEM_102" },
{ RDP_SCANCODE_F24_JP, "JP F24" },
{ RDP_SCANCODE_CONVERT_JP, "JP VK_CONVERT" },
{ RDP_SCANCODE_NONCONVERT_JP, "JP VK_NONCONVERT" },
{ RDP_SCANCODE_TAB_JP, "JP TAB" },
{ RDP_SCANCODE_BACKSLASH_JP, "JP OEM_5 ('\')" },
{ RDP_SCANCODE_ABNT_C2, "VK_ABNT_C2, JP" },
{ RDP_SCANCODE_HANJA, "KR VK_HANJA" },
{ RDP_SCANCODE_HANGUL, "KR VK_HANGUL" },
{ RDP_SCANCODE_RETURN_KP, "not RDP_SCANCODE_RETURN Numerical Enter" },
{ RDP_SCANCODE_RCONTROL, "VK_RCONTROL" },
{ RDP_SCANCODE_DIVIDE, "VK_DIVIDE Numerical" },
{ RDP_SCANCODE_PRINTSCREEN, "VK_EXECUTE/VK_PRINT/VK_SNAPSHOT Print Screen" },
{ RDP_SCANCODE_RMENU, "VK_RMENU Right 'Alt' / 'Alt Gr'" },
{ RDP_SCANCODE_PAUSE, "VK_PAUSE Pause / Break (Slightly special handling)" },
{ RDP_SCANCODE_HOME, "VK_HOME" },
{ RDP_SCANCODE_UP, "VK_UP" },
{ RDP_SCANCODE_PRIOR, "VK_PRIOR 'Page Up'" },
{ RDP_SCANCODE_LEFT, "VK_LEFT" },
{ RDP_SCANCODE_RIGHT, "VK_RIGHT" },
{ RDP_SCANCODE_END, "VK_END" },
{ RDP_SCANCODE_DOWN, "VK_DOWN" },
{ RDP_SCANCODE_NEXT, "VK_NEXT 'Page Down'" },
{ RDP_SCANCODE_INSERT, "VK_INSERT" },
{ RDP_SCANCODE_DELETE, "VK_DELETE" },
{ RDP_SCANCODE_NULL, "<00>" },
{ RDP_SCANCODE_HELP2, "Help - documented, different from VK_HELP" },
{ RDP_SCANCODE_LWIN, "VK_LWIN" },
{ RDP_SCANCODE_RWIN, "VK_RWIN" },
{ RDP_SCANCODE_APPS, "VK_APPS Application" },
{ RDP_SCANCODE_POWER_JP, "JP POWER" },
{ RDP_SCANCODE_SLEEP_JP, "JP SLEEP" },
{ RDP_SCANCODE_NUMLOCK_EXTENDED, "should be RDP_SCANCODE_NUMLOCK" },
{ RDP_SCANCODE_RSHIFT_EXTENDED, "should be RDP_SCANCODE_RSHIFT" },
{ RDP_SCANCODE_VOLUME_MUTE, "VK_VOLUME_MUTE" },
{ RDP_SCANCODE_VOLUME_DOWN, "VK_VOLUME_DOWN" },
{ RDP_SCANCODE_VOLUME_UP, "VK_VOLUME_UP" },
{ RDP_SCANCODE_MEDIA_NEXT_TRACK, "VK_MEDIA_NEXT_TRACK" },
{ RDP_SCANCODE_MEDIA_PREV_TRACK, "VK_MEDIA_PREV_TRACK" },
{ RDP_SCANCODE_MEDIA_STOP, "VK_MEDIA_MEDIA_STOP" },
{ RDP_SCANCODE_MEDIA_PLAY_PAUSE, "VK_MEDIA_MEDIA_PLAY_PAUSE" },
{ RDP_SCANCODE_BROWSER_BACK, "VK_BROWSER_BACK" },
{ RDP_SCANCODE_BROWSER_FORWARD, "VK_BROWSER_FORWARD" },
{ RDP_SCANCODE_BROWSER_REFRESH, "VK_BROWSER_REFRESH" },
{ RDP_SCANCODE_BROWSER_STOP, "VK_BROWSER_STOP" },
{ RDP_SCANCODE_BROWSER_SEARCH, "VK_BROWSER_SEARCH" },
{ RDP_SCANCODE_BROWSER_FAVORITES, "VK_BROWSER_FAVORITES" },
{ RDP_SCANCODE_BROWSER_HOME, "VK_BROWSER_HOME" },
{ RDP_SCANCODE_LAUNCH_MAIL, "VK_LAUNCH_MAIL" },
{ RDP_SCANCODE_LAUNCH_MEDIA_SELECT, "VK_LAUNCH_MEDIA_SELECT" },
{ RDP_SCANCODE_LAUNCH_APP1, "VK_LAUNCH_APP1" },
{ RDP_SCANCODE_LAUNCH_APP2, "VK_LAUNCH_APP2" },
};
#if !defined(WITHOUT_FREERDP_3x_DEPRECATED)
static int freerdp_detect_keyboard(DWORD* keyboardLayoutId)
{
#if defined(_WIN32)
CHAR name[KL_NAMELENGTH + 1] = WINPR_C_ARRAY_INIT;
if (GetKeyboardLayoutNameA(name))
{
ULONG rc;
errno = 0;
rc = strtoul(name, nullptr, 16);
if (errno == 0)
*keyboardLayoutId = rc;
}
if (*keyboardLayoutId == 0)
{
const HKL layout = GetKeyboardLayout(0);
const uint32_t masked = (uint32_t)(((uintptr_t)layout >> 16) & 0xFFFF);
*keyboardLayoutId = masked;
}
#endif
#if defined(__MACOSX__)
if (*keyboardLayoutId == 0)
freerdp_detect_keyboard_layout_from_cf(keyboardLayoutId);
#endif
#ifdef WITH_X11
if (*keyboardLayoutId == 0)
freerdp_detect_keyboard_layout_from_xkb(keyboardLayoutId);
#endif
if (*keyboardLayoutId == 0)
freerdp_detect_keyboard_layout_from_system_locale(keyboardLayoutId);
if (*keyboardLayoutId == 0)
*keyboardLayoutId = ENGLISH_UNITED_STATES;
return 0;
}
#if defined(__APPLE__)
static int freerdp_keyboard_init_apple(WINPR_ATTR_UNUSED const DWORD* keyboardLayoutId,
DWORD* x11_keycode_to_rdp_scancode, size_t count)
{
WINPR_ASSERT(x11_keycode_to_rdp_scancode);
WINPR_ASSERT(keyboardLayoutId);
WINPR_ASSERT(count <= UINT32_MAX);
for (size_t keycode = 8; keycode < count; keycode++)
{
const DWORD vkcode =
GetVirtualKeyCodeFromKeycode((UINT32)keycode - 8u, WINPR_KEYCODE_TYPE_APPLE);
x11_keycode_to_rdp_scancode[keycode] =
GetVirtualScanCodeFromVirtualKeyCode(vkcode, WINPR_KBD_TYPE_IBM_ENHANCED);
}
maptype = WINPR_KEYCODE_TYPE_APPLE;
return 0;
}
#endif
static int freerdp_keyboard_init_x11_evdev(WINPR_ATTR_UNUSED const DWORD* keyboardLayoutId,
DWORD* x11_keycode_to_rdp_scancode, size_t count)
{
WINPR_ASSERT(keyboardLayoutId);
WINPR_ASSERT(x11_keycode_to_rdp_scancode);
WINPR_ASSERT(count <= UINT32_MAX);
for (size_t keycode = 0; keycode < count; keycode++)
{
const DWORD vkcode =
GetVirtualKeyCodeFromKeycode((UINT32)keycode, WINPR_KEYCODE_TYPE_EVDEV);
x11_keycode_to_rdp_scancode[keycode] =
GetVirtualScanCodeFromVirtualKeyCode(vkcode, WINPR_KBD_TYPE_IBM_ENHANCED);
}
maptype = WINPR_KEYCODE_TYPE_EVDEV;
return 0;
}
DWORD freerdp_keyboard_init(DWORD keyboardLayoutId)
{
int status = -1;
#if defined(__APPLE__)
if (status < 0)
status = freerdp_keyboard_init_apple(&keyboardLayoutId, X11_KEYCODE_TO_VIRTUAL_SCANCODE,
ARRAYSIZE(X11_KEYCODE_TO_VIRTUAL_SCANCODE));
#endif
#if defined(WITH_X11) || defined(WITH_WAYLAND)
#ifdef WITH_XKBFILE
if (status < 0)
{
status = freerdp_keyboard_init_xkbfile(&keyboardLayoutId, X11_KEYCODE_TO_VIRTUAL_SCANCODE,
ARRAYSIZE(X11_KEYCODE_TO_VIRTUAL_SCANCODE));
if (status >= 0)
maptype = WINPR_KEYCODE_TYPE_XKB;
}
#endif
if (status < 0)
status = freerdp_keyboard_init_x11_evdev(&keyboardLayoutId, X11_KEYCODE_TO_VIRTUAL_SCANCODE,
ARRAYSIZE(X11_KEYCODE_TO_VIRTUAL_SCANCODE));
#endif
if (status < 0)
WLog_DBG(TAG, "Platform keyboard detection failed, trying autodetection");
freerdp_detect_keyboard(&keyboardLayoutId);
ZeroMemory(VIRTUAL_SCANCODE_TO_X11_KEYCODE, sizeof(VIRTUAL_SCANCODE_TO_X11_KEYCODE));
WINPR_STATIC_ASSERT(ARRAYSIZE(VIRTUAL_SCANCODE_TO_X11_KEYCODE) <= UINT32_MAX);
for (size_t keycode = 0; keycode < ARRAYSIZE(VIRTUAL_SCANCODE_TO_X11_KEYCODE); keycode++)
{
const DWORD x11 = X11_KEYCODE_TO_VIRTUAL_SCANCODE[keycode];
const DWORD sc = RDP_SCANCODE_CODE(x11);
const BOOL ex = RDP_SCANCODE_EXTENDED(x11);
VIRTUAL_SCANCODE_TO_X11_KEYCODE[sc][ex ? 1 : 0] = (UINT32)keycode;
}
return keyboardLayoutId;
}
#endif
FREERDP_REMAP_TABLE* freerdp_keyboard_remap_string_to_list(const char* list)
{
const size_t remap_table_size = 0x10000;
FREERDP_REMAP_TABLE* remap_table = calloc(1, sizeof(FREERDP_REMAP_TABLE));
if (!remap_table)
return nullptr;
for (size_t x = 0; x < ARRAYSIZE(remap_table->table); x++)
remap_table->table[x] = (UINT32)x;
if (!list)
return remap_table;
BOOL success = FALSE;
char* copy = _strdup(list);
if (!copy)
goto fail;
{
char* context = nullptr;
char* token = strtok_s(copy, ",", &context);
while (token)
{
UINT32 key = 0;
UINT32 value = 0;
if (!freerdp_extract_key_value(token, &key, &value))
goto fail;
if (key >= remap_table_size)
goto fail;
remap_table->table[key] = value;
token = strtok_s(nullptr, ",", &context);
}
}
success = TRUE;
fail:
free(copy);
if (!success)
{
free(remap_table);
return nullptr;
}
return remap_table;
}
#if !defined(WITHOUT_FREERDP_3x_DEPRECATED)
DWORD freerdp_keyboard_init_ex(DWORD keyboardLayoutId, const char* keyboardRemappingList)
{
DWORD res = freerdp_keyboard_init(keyboardLayoutId);
memset(REMAPPING_TABLE, 0, sizeof(REMAPPING_TABLE));
if (keyboardRemappingList)
{
char* copy = _strdup(keyboardRemappingList);
char* context = nullptr;
char* token = nullptr;
if (!copy)
goto fail;
token = strtok_s(copy, ",", &context);
while (token)
{
DWORD key = 0;
DWORD value = 0;
if (!freerdp_extract_key_value(token, &key, &value))
goto fail;
if (key >= ARRAYSIZE(REMAPPING_TABLE))
goto fail;
REMAPPING_TABLE[key] = value;
token = strtok_s(nullptr, ",", &context);
}
fail:
free(copy);
}
return res;
}
DWORD freerdp_keyboard_get_rdp_scancode_from_x11_keycode(DWORD keycode)
{
if (keycode >= ARRAYSIZE(X11_KEYCODE_TO_VIRTUAL_SCANCODE))
{
WLog_ERR(TAG, "KeyCode %" PRIu32 " exceeds allowed value range [0,%" PRIuz "]", keycode,
ARRAYSIZE(X11_KEYCODE_TO_VIRTUAL_SCANCODE));
return 0;
}
const DWORD scancode = X11_KEYCODE_TO_VIRTUAL_SCANCODE[keycode];
if (scancode >= ARRAYSIZE(REMAPPING_TABLE))
{
WLog_ERR(TAG, "ScanCode %" PRIu32 " exceeds allowed value range [0,%" PRIuz "]", scancode,
ARRAYSIZE(REMAPPING_TABLE));
return 0;
}
const DWORD remapped = REMAPPING_TABLE[scancode];
#if defined(WITH_DEBUG_KBD)
const BOOL ex = RDP_SCANCODE_EXTENDED(scancode);
const DWORD sc = RDP_SCANCODE_CODE(scancode);
#endif
DEBUG_KBD("x11 keycode: %02" PRIX32 " -> rdp code: [%04" PRIx16 "] %02" PRIX8 "%s", keycode,
scancode, sc, ex ? " extended" : "");
if (remapped != 0)
{
#if defined(WITH_DEBUG_KBD)
const DWORD rsc = RDP_SCANCODE_CODE(remapped);
const BOOL rex = RDP_SCANCODE_EXTENDED(remapped);
#endif
DEBUG_KBD("remapped scancode: [%04" PRIx16 "] %02" PRIX8 "[%s] -> [%04" PRIx16 "] %02" PRIX8
"[%s]",
scancode, sc, ex ? " extended" : "", remapped, rsc, rex ? " extended" : "");
return remapped;
}
return scancode;
}
DWORD freerdp_keyboard_get_x11_keycode_from_rdp_scancode(DWORD scancode, BOOL extended)
{
if (scancode >= ARRAYSIZE(VIRTUAL_SCANCODE_TO_X11_KEYCODE))
{
WLog_ERR(TAG, "ScanCode %" PRIu32 " exceeds allowed value range [0,%" PRIuz "]", scancode,
ARRAYSIZE(VIRTUAL_SCANCODE_TO_X11_KEYCODE));
return 0;
}
const DWORD* x11 = VIRTUAL_SCANCODE_TO_X11_KEYCODE[scancode];
WINPR_ASSERT(x11);
if (extended)
return x11[1];
else
return x11[0];
}
#endif
const char* freerdp_keyboard_scancode_name(DWORD scancode)
{
for (size_t x = 0; x < ARRAYSIZE(RDP_SCANCODE_MAP); x++)
{
const struct scancode_map_entry* entry = &RDP_SCANCODE_MAP[x];
if (entry->scancode == scancode)
return entry->name;
}
return nullptr;
}
DWORD freerdp_keyboard_remap_key(const FREERDP_REMAP_TABLE* remap_table, DWORD rdpScanCode)
{
if (!remap_table || (ARRAYSIZE(remap_table->table) <= rdpScanCode))
return 0;
return remap_table->table[rdpScanCode];
}
void freerdp_keyboard_remap_free(FREERDP_REMAP_TABLE* table)
{
free(table);
}

View File

@@ -0,0 +1,241 @@
/**
* FreeRDP: A Remote Desktop Protocol Implementation
* Apple Core Foundation Keyboard Mapping
*
* Copyright 2021 Thincast Technologies GmbH
* Copyright 2021 Martin Fleisz <martin.fleisz@thincast.com>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <freerdp/config.h>
#include <Carbon/Carbon.h>
#include <string.h>
#include "liblocale.h"
#include <freerdp/locale/locale.h>
#include <freerdp/locale/keyboard.h>
#include "keyboard_apple.h"
struct KEYBOARD_LAYOUT_MAPPING_
{
const char* inputSourceId; /* Apple input source id (com.apple.keylayout or inputmethod) */
DWORD code; /* mapped rdp keyboard layout id */
};
typedef struct KEYBOARD_LAYOUT_MAPPING_ KEYBOARD_LAYOUT_MAPPING;
static const KEYBOARD_LAYOUT_MAPPING KEYBOARD_MAPPING_TABLE[] = {
{ "com.apple.inputmethod.Kotoeri.Japanese", JAPANESE },
{ "com.apple.inputmethod.Kotoeri.Japanese.FullWidthRoman", JAPANESE },
{ "com.apple.inputmethod.Kotoeri.Japanese.HalfWidthKana", JAPANESE },
{ "com.apple.inputmethod.Kotoeri.Japanese.Katakana", JAPANESE },
{ "com.apple.inputmethod.Kotoeri.Katakana", JAPANESE },
{ "com.apple.inputmethod.Kotoeri.Roman", JAPANESE },
{ "com.apple.inputmethod.kotoeri.Ainu", JAPANESE },
{ "com.apple.keylayout.2SetHangul", KOREAN },
{ "com.apple.keylayout.390Hangul", KOREAN },
{ "com.apple.keylayout.3SetHangul", KOREAN },
{ "com.apple.keylayout.AfghanDari", KBD_PERSIAN },
{ "com.apple.keylayout.AfghanPashto", PASHTO },
{ "com.apple.keylayout.AfghanUzbek", UZBEK_LATIN },
{ "com.apple.keylayout.Arabic", ARABIC_SAUDI_ARABIA },
{ "com.apple.keylayout.Arabic-QWERTY", ARABIC_EGYPT },
{ "com.apple.keylayout.ArabicPC", ARABIC_EGYPT },
{ "com.apple.keylayout.Armenian-HMQWERTY", ARMENIAN },
{ "com.apple.keylayout.Armenian-WesternQWERTY", ARMENIAN },
{ "com.apple.keylayout.Australian", ENGLISH_AUSTRALIAN },
{ "com.apple.keylayout.Austrian", GERMAN_STANDARD },
{ "com.apple.keylayout.Azeri", AZERI_LATIN },
{ "com.apple.keylayout.Bangla", KBD_BANGLA },
{ "com.apple.keylayout.Bangla-QWERTY", KBD_BANGLA },
{ "com.apple.keylayout.Belgian", DUTCH_BELGIAN },
{ "com.apple.keylayout.Brazilian", PORTUGUESE_BRAZILIAN },
{ "com.apple.keylayout.British", ENGLISH_UNITED_KINGDOM },
{ "com.apple.keylayout.British-PC", ENGLISH_UNITED_KINGDOM },
{ "com.apple.keylayout.Bulgarian", BULGARIAN },
{ "com.apple.keylayout.Bulgarian-Phonetic", KBD_BULGARIAN_PHONETIC },
{ "com.apple.keylayout.Byelorussian", BELARUSIAN },
{ "com.apple.keylayout.Canadian", ENGLISH_UNITED_STATES },
{ "com.apple.keylayout.Canadian-CSA", KBD_CANADIAN_MULTILINGUAL_STANDARD },
{ "com.apple.keylayout.CangjieKeyboard", CHINESE_TAIWAN },
{ "com.apple.keylayout.Cherokee-Nation", CHEROKEE },
{ "com.apple.keylayout.Cherokee-QWERTY", ENGLISH_UNITED_STATES },
{ "com.apple.keylayout.Colemak", ENGLISH_UNITED_STATES },
{ "com.apple.keylayout.Croatian", CROATIAN },
{ "com.apple.keylayout.Croatian-PC", CROATIAN },
{ "com.apple.keylayout.Czech", CZECH },
{ "com.apple.keylayout.Czech-QWERTY", KBD_CZECH_QWERTY },
{ "com.apple.keylayout.DVORAK-QWERTYCMD", KBD_UNITED_STATES_DVORAK },
{ "com.apple.keylayout.Danish", DANISH },
{ "com.apple.keylayout.Devanagari", HINDI },
{ "com.apple.keylayout.Devanagari-QWERTY", HINDI },
{ "com.apple.keylayout.Dutch", KBD_UNITED_STATES_INTERNATIONAL },
{ "com.apple.keylayout.Dvorak", KBD_UNITED_STATES_DVORAK },
{ "com.apple.keylayout.Dvorak-Left", KBD_UNITED_STATES_DVORAK_FOR_LEFT_HAND },
{ "com.apple.keylayout.Dvorak-Right", KBD_UNITED_STATES_DVORAK_FOR_RIGHT_HAND },
{ "com.apple.keylayout.Estonian", ESTONIAN },
{ "com.apple.keylayout.Faroese", FAEROESE },
{ "com.apple.keylayout.Finnish", FINNISH },
{ "com.apple.keylayout.FinnishExtended", KBD_SAMI_EXTENDED_FINLAND_SWEDEN },
{ "com.apple.keylayout.FinnishSami-PC", KBD_FINNISH_WITH_SAMI },
{ "com.apple.keylayout.French", KBD_BELGIAN_FRENCH },
{ "com.apple.keylayout.French-PC", FRENCH_STANDARD },
{ "com.apple.keylayout.French-numerical", KBD_BELGIAN_FRENCH },
{ "com.apple.keylayout.GJCRomaja", ENGLISH_UNITED_STATES },
{ "com.apple.keylayout.Georgian-QWERTY", KBD_GEORGIAN_QUERTY },
{ "com.apple.keylayout.German", GERMAN_STANDARD },
{ "com.apple.keylayout.Greek", GREEK },
{ "com.apple.keylayout.GreekPolytonic", KBD_GREEK_POLYTONIC },
{ "com.apple.keylayout.Gujarati", GUJARATI },
{ "com.apple.keylayout.Gujarati-QWERTY", GUJARATI },
{ "com.apple.keylayout.Gurmukhi", PUNJABI },
{ "com.apple.keylayout.Gurmukhi-QWERTY", PUNJABI },
{ "com.apple.keylayout.HNCRomaja", ENGLISH_UNITED_STATES },
{ "com.apple.keylayout.Hawaiian", HAWAIIAN },
{ "com.apple.keylayout.Hebrew", HEBREW },
{ "com.apple.keylayout.Hebrew-PC", HEBREW },
{ "com.apple.keylayout.Hebrew-QWERTY", HEBREW },
{ "com.apple.keylayout.Hungarian", HUNGARIAN },
{ "com.apple.keylayout.Hungarian-QWERTY", HUNGARIAN },
{ "com.apple.keylayout.Icelandic", ICELANDIC },
{ "com.apple.keylayout.Inuktitut-Nunavut", INUKTITUT },
{ "com.apple.keylayout.Inuktitut-Nutaaq", INUKTITUT },
{ "com.apple.keylayout.Inuktitut-QWERTY", INUKTITUT },
{ "com.apple.keylayout.InuttitutNunavik", INUKTITUT },
{ "com.apple.keylayout.Irish", ENGLISH_IRELAND },
{ "com.apple.keylayout.IrishExtended", KBD_IRISH },
{ "com.apple.keylayout.Italian", ITALIAN_STANDARD },
{ "com.apple.keylayout.Italian-Pro", ITALIAN_STANDARD },
{ "com.apple.keylayout.Jawi-QWERTY", ARABIC_SAUDI_ARABIA },
{ "com.apple.keylayout.Kannada", KANNADA },
{ "com.apple.keylayout.Kannada-QWERTY", KANNADA },
{ "com.apple.keylayout.Kazakh", KAZAKH },
{ "com.apple.keylayout.Khmer", KBD_KHMER },
{ "com.apple.keylayout.Latvian", LATVIAN },
{ "com.apple.keylayout.Lithuanian", LITHUANIAN },
{ "com.apple.keylayout.Macedonian", MACEDONIAN },
{ "com.apple.keylayout.Malayalam", MALAYALAM },
{ "com.apple.keylayout.Malayalam-QWERTY", MALAYALAM },
{ "com.apple.keylayout.Maltese", MALTESE },
{ "com.apple.keylayout.Maori", MAORI },
{ "com.apple.keylayout.Myanmar-QWERTY", MYANMAR },
{ "com.apple.keylayout.Nepali", NEPALI },
{ "com.apple.keylayout.NorthernSami", SAMI_NORTHERN_NORWAY },
{ "com.apple.keylayout.Norwegian", NORWEGIAN_BOKMAL },
{ "com.apple.keylayout.NorwegianExtended", NORWEGIAN_BOKMAL },
{ "com.apple.keylayout.NorwegianSami-PC", NORWEGIAN_BOKMAL },
{ "com.apple.keylayout.Oriya", ORIYA },
{ "com.apple.keylayout.Persian", KBD_PERSIAN },
{ "com.apple.keylayout.Persian-ISIRI2901", KBD_PERSIAN },
{ "com.apple.keylayout.Polish", KBD_POLISH_214 },
{ "com.apple.keylayout.PolishPro", KBD_POLISH_PROGRAMMERS },
{ "com.apple.keylayout.Portuguese", PORTUGUESE_STANDARD },
{ "com.apple.keylayout.Romanian", KBD_ROMANIAN },
{ "com.apple.keylayout.Romanian-Standard", KBD_ROMANIAN_STANDARD },
{ "com.apple.keylayout.Russian", RUSSIAN },
{ "com.apple.keylayout.Russian-Phonetic", KBD_RUSSIAN_PHONETIC },
{ "com.apple.keylayout.RussianWin", RUSSIAN },
{ "com.apple.keylayout.Sami-PC", KBD_SAMI_EXTENDED_FINLAND_SWEDEN },
{ "com.apple.keylayout.Serbian", KBD_SERBIAN_CYRILLIC },
{ "com.apple.keylayout.Serbian-Latin", KBD_SERBIAN_LATIN },
{ "com.apple.keylayout.Sinhala", SINHALA },
{ "com.apple.keylayout.Sinhala-QWERTY", SINHALA },
{ "com.apple.keylayout.Slovak", SLOVAK },
{ "com.apple.keylayout.Slovak-QWERTY", KBD_SLOVAK_QWERTY },
{ "com.apple.keylayout.Slovenian", SLOVENIAN },
{ "com.apple.keylayout.Spanish", SPANISH_TRADITIONAL_SORT },
{ "com.apple.keylayout.Spanish-ISO", KBD_SPANISH },
{ "com.apple.keylayout.Swedish", SWEDISH },
{ "com.apple.keylayout.Swedish-Pro", SWEDISH },
{ "com.apple.keylayout.SwedishSami-PC", SWEDISH },
{ "com.apple.keylayout.SwissFrench", FRENCH_SWISS },
{ "com.apple.keylayout.SwissGerman", GERMAN_SWISS },
{ "com.apple.keylayout.Telugu", TELUGU },
{ "com.apple.keylayout.Telugu-QWERTY", TELUGU },
{ "com.apple.keylayout.Thai", THAI },
{ "com.apple.keylayout.Thai-PattaChote", KBD_THAI_PATTACHOTE },
{ "com.apple.keylayout.Tibetan-QWERTY", TIBETAN_PRC },
{ "com.apple.keylayout.Tibetan-Wylie", TIBETAN_PRC },
{ "com.apple.keylayout.TibetanOtaniUS", TIBETAN_PRC },
{ "com.apple.keylayout.Turkish", KBD_TURKISH_F },
{ "com.apple.keylayout.Turkish-QWERTY", TURKISH },
{ "com.apple.keylayout.Turkish-QWERTY-PC", TURKISH },
{ "com.apple.keylayout.US", ENGLISH_UNITED_STATES },
{ "com.apple.keylayout.USExtended", ENGLISH_UNITED_STATES },
{ "com.apple.keylayout.USInternational-PC", ENGLISH_UNITED_STATES },
{ "com.apple.keylayout.Ukrainian", UKRAINIAN },
{ "com.apple.keylayout.Ukrainian-PC", UKRAINIAN },
{ "com.apple.keylayout.UnicodeHexInput", ENGLISH_UNITED_STATES },
{ "com.apple.keylayout.Urdu", URDU },
{ "com.apple.keylayout.Uyghur", UIGHUR },
{ "com.apple.keylayout.Vietnamese", VIETNAMESE },
{ "com.apple.keylayout.Welsh", WELSH }
};
int freerdp_detect_keyboard_layout_from_cf(DWORD* keyboardLayoutId)
{
CFIndex length;
char* inputSourceId = nullptr;
CFStringRef inputSourceIdRef;
TISInputSourceRef inputSrc = TISCopyCurrentKeyboardLayoutInputSource();
if (!inputSrc)
{
DEBUG_KBD("Failed to get current keyboard layout input source!");
return 0;
}
/* get current input source id */
inputSourceIdRef = (CFStringRef)TISGetInputSourceProperty(inputSrc, kTISPropertyInputSourceID);
if (!inputSourceIdRef)
{
DEBUG_KBD("Failed to get input source id!");
goto done;
}
/* convert it to a C-string */
length = CFStringGetLength(inputSourceIdRef);
length = CFStringGetMaximumSizeForEncoding(length, kCFStringEncodingUTF8) + 1;
inputSourceId = (char*)malloc(length);
if (!inputSourceId)
{
DEBUG_KBD("Failed to allocate string buffer!");
goto done;
}
if (!CFStringGetCString(inputSourceIdRef, inputSourceId, length, kCFStringEncodingUTF8))
{
DEBUG_KBD("Failed to convert CFString to C-string!");
goto done;
}
/* Search for the id in the mapping table */
for (size_t i = 0; i < ARRAYSIZE(KEYBOARD_MAPPING_TABLE); ++i)
{
if (strcmp(inputSourceId, KEYBOARD_MAPPING_TABLE[i].inputSourceId) == 0)
{
*keyboardLayoutId = KEYBOARD_MAPPING_TABLE[i].code;
break;
}
}
done:
free(inputSourceId);
CFRelease(inputSrc);
if (*keyboardLayoutId > 0)
return *keyboardLayoutId;
return 0;
}

View File

@@ -0,0 +1,28 @@
/**
* FreeRDP: A Remote Desktop Protocol Implementation
* Apple Core Foundation Keyboard Mapping
*
* Copyright 2021 Thincast Technologies GmbH
* Copyright 2021 Martin Fleisz <martin.fleisz@thincast.com>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef FREERDP_LOCALE_KEYBOARD_APPLE_H
#define FREERDP_LOCALE_KEYBOARD_APPLE_H
#include <freerdp/api.h>
FREERDP_LOCAL int freerdp_detect_keyboard_layout_from_cf(DWORD* keyboardLayoutId);
#endif /* FREERDP_LOCALE_KEYBOARD_APPLE_H */

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,284 @@
/**
* FreeRDP: A Remote Desktop Protocol Implementation
* Solaris Keyboard Mapping
*
* Copyright 2009-2012 Marc-Andre Moreau <marcandre.moreau@gmail.com>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <freerdp/config.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <winpr/crt.h>
#include "liblocale.h"
#include <freerdp/locale/keyboard.h>
#include "keyboard_x11.h"
#include "keyboard_sun.h"
/* OpenSolaris 2008.11 and 2009.06 keyboard layouts
*
* While OpenSolaris comes with Xorg and XKB, it maintains a set of keyboard layout
* names that map directly to a particular keyboard layout in XKB. Fortunately for us,
* this way of doing things comes from Solaris, which is XKB unaware. The same keyboard
* layout naming system is used in Solaris, so we can use the same XKB configuration as
* we would on OpenSolaris and get an accurate keyboard layout detection :)
*
* We can check for the current keyboard layout using the "kbd -l" command:
*
* type=6
* layout=33 (0x21)
* delay(ms)=500
* rate(ms)=40
*
* We can check at runtime if the kbd utility is present, parse the output, and use the
* keyboard layout indicated by the index given (in this case, 33, or US-English).
*/
typedef struct
{
UINT32 type; /* Solaris keyboard type */
UINT32 layout; /* Layout */
char* xkbType; /* XKB keyboard */
UINT32 keyboardLayoutId; /* XKB keyboard layout */
} SOLARIS_KEYBOARD;
static const SOLARIS_KEYBOARD SOLARIS_KEYBOARD_TABLE[] = {
{ 4, 0, "sun(type4)", KBD_US }, /* US4 */
{ 4, 1, "sun(type4)", KBD_US }, /* US4 */
{ 4, 2, "sun(type4tuv)", KBD_FRENCH }, /* FranceBelg4 */
{ 4, 3, "sun(type4_ca)", KBD_US }, /* Canada4 */
{ 4, 4, "sun(type4tuv)", KBD_DANISH }, /* Denmark4 */
{ 4, 5, "sun(type4tuv)", KBD_GERMAN }, /* Germany4 */
{ 4, 6, "sun(type4tuv)", KBD_ITALIAN }, /* Italy4 */
{ 4, 7, "sun(type4tuv)", KBD_DUTCH }, /* Netherland4 */
{ 4, 8, "sun(type4tuv)", KBD_NORWEGIAN }, /* Norway4 */
{ 4, 9, "sun(type4tuv)", KBD_PORTUGUESE }, /* Portugal4 */
{ 4, 10, "sun(type4tuv)", KBD_SPANISH }, /* SpainLatAm4 */
{ 4, 11, "sun(type4tuv)", KBD_SWEDISH }, /* SwedenFin4 */
{ 4, 12, "sun(type4tuv)", KBD_SWISS_FRENCH }, /* Switzer_Fr4 */
{ 4, 13, "sun(type4tuv)", KBD_SWISS_GERMAN }, /* Switzer_Ge4 */
{ 4, 14, "sun(type4tuv)", KBD_UNITED_KINGDOM }, /* UK4 */
{ 4, 16, "sun(type4)", KBD_KOREAN_INPUT_SYSTEM_IME_2000 }, /* Korea4 */
{ 4, 17, "sun(type4)", KBD_CHINESE_TRADITIONAL_PHONETIC }, /* Taiwan4 */
{ 4, 32, "sun(type4jp)", KBD_JAPANESE_INPUT_SYSTEM_MS_IME2002 }, /* Japan4 */
{ 4, 19, "sun(type5)", KBD_US }, /* US101A_PC */
{ 4, 33, "sun(type5)", KBD_US }, /* US5 */
{ 4, 34, "sun(type5unix)", KBD_US }, /* US_UNIX5 */
{ 4, 35, "sun(type5tuv)", KBD_FRENCH }, /* France5 */
{ 4, 36, "sun(type5tuv)", KBD_DANISH }, /* Denmark5 */
{ 4, 37, "sun(type5tuv)", KBD_GERMAN }, /* Germany5 */
{ 4, 38, "sun(type5tuv)", KBD_ITALIAN }, /* Italy5 */
{ 4, 39, "sun(type5tuv)", KBD_DUTCH }, /* Netherland5 */
{ 4, 40, "sun(type5tuv)", KBD_NORWEGIAN }, /* Norway5 */
{ 4, 41, "sun(type5tuv)", KBD_PORTUGUESE }, /* Portugal5 */
{ 4, 42, "sun(type5tuv)", KBD_SPANISH }, /* Spain5 */
{ 4, 43, "sun(type5tuv)", KBD_SWEDISH }, /* Sweden5 */
{ 4, 44, "sun(type5tuv)", KBD_SWISS_FRENCH }, /* Switzer_Fr5 */
{ 4, 45, "sun(type5tuv)", KBD_SWISS_GERMAN }, /* Switzer_Ge5 */
{ 4, 46, "sun(type5tuv)", KBD_UNITED_KINGDOM }, /* UK5 */
{ 4, 47, "sun(type5)", KBD_KOREAN_INPUT_SYSTEM_IME_2000 }, /* Korea5 */
{ 4, 48, "sun(type5)", KBD_CHINESE_TRADITIONAL_PHONETIC }, /* Taiwan5 */
{ 4, 49, "sun(type5jp)", KBD_JAPANESE_INPUT_SYSTEM_MS_IME2002 }, /* Japan5 */
{ 4, 50, "sun(type5tuv)", KBD_CANADIAN_FRENCH }, /* Canada_Fr5 */
{ 4, 51, "sun(type5tuv)", KBD_HUNGARIAN }, /* Hungary5 */
{ 4, 52, "sun(type5tuv)", KBD_POLISH_214 }, /* Poland5 */
{ 4, 53, "sun(type5tuv)", KBD_CZECH }, /* Czech5 */
{ 4, 54, "sun(type5tuv)", KBD_RUSSIAN }, /* Russia5 */
{ 4, 55, "sun(type5tuv)", KBD_LATVIAN }, /* Latvia5 */
{ 4, 57, "sun(type5tuv)", KBD_GREEK }, /* Greece5 */
{ 4, 59, "sun(type5tuv)", KBD_LITHUANIAN }, /* Lithuania5 */
{ 4, 63, "sun(type5tuv)", KBD_CANADIAN_FRENCH }, /* Canada_Fr5_TBITS5 */
{ 4, 56, "sun(type5tuv)", KBD_TURKISH_Q }, /* TurkeyQ5 */
{ 4, 58, "sun(type5tuv)", KBD_ARABIC_101 }, /* Arabic5 */
{ 4, 60, "sun(type5tuv)", KBD_BELGIAN_FRENCH }, /* Belgian5 */
{ 4, 62, "sun(type5tuv)", KBD_TURKISH_F }, /* TurkeyF5 */
{ 4, 80, "sun(type5hobo)", KBD_US }, /* US5_Hobo */
{ 4, 81, "sun(type5hobo)", KBD_US }, /* US_UNIX5_Hobo */
{ 4, 82, "sun(type5tuvhobo)", KBD_FRENCH }, /* France5_Hobo */
{ 4, 83, "sun(type5tuvhobo)", KBD_DANISH }, /* Denmark5_Hobo */
{ 4, 84, "sun(type5tuvhobo)", KBD_GERMAN }, /* Germany5_Hobo */
{ 4, 85, "sun(type5tuvhobo)", KBD_ITALIAN }, /* Italy5_Hobo */
{ 4, 86, "sun(type5tuvhobo)", KBD_DUTCH }, /* Netherland5_Hobo */
{ 4, 87, "sun(type5tuvhobo)", KBD_NORWEGIAN }, /* Norway5_Hobo */
{ 4, 88, "sun(type5tuvhobo)", KBD_PORTUGUESE }, /* Portugal5_Hobo */
{ 4, 89, "sun(type5tuvhobo)", KBD_SPANISH }, /* Spain5_Hobo */
{ 4, 90, "sun(type5tuvhobo)", KBD_SWEDISH }, /* Sweden5_Hobo */
{ 4, 91, "sun(type5tuvhobo)", KBD_SWISS_FRENCH }, /* Switzer_Fr5_Hobo */
{ 4, 92, "sun(type5tuvhobo)", KBD_SWISS_GERMAN }, /* Switzer_Ge5_Hobo */
{ 4, 93, "sun(type5tuvhobo)", KBD_UNITED_KINGDOM }, /* UK5_Hobo */
{ 4, 94, "sun(type5hobo)", KBD_KOREAN_INPUT_SYSTEM_IME_2000 }, /* Korea5_Hobo */
{ 4, 95, "sun(type5hobo)", KBD_CHINESE_TRADITIONAL_PHONETIC }, /* Taiwan5_Hobo */
{ 4, 96, "sun(type5jphobo)", KBD_JAPANESE_INPUT_SYSTEM_MS_IME2002 }, /* Japan5_Hobo */
{ 4, 97, "sun(type5tuvhobo)", KBD_CANADIAN_FRENCH }, /* Canada_Fr5_Hobo */
{ 101, 1, "digital_vndr/pc(pc104)", KBD_US }, /* US101A_x86 */
{ 101, 34, "digital_vndr/pc(pc104)", KBD_US }, /* J3100_x86 */
{ 101, 35, "digital_vndr/pc(pc104)", KBD_FRENCH }, /* France_x86 */
{ 101, 36, "digital_vndr/pc(pc104)", KBD_DANISH }, /* Denmark_x86 */
{ 101, 37, "digital_vndr/pc(pc104)", KBD_GERMAN }, /* Germany_x86 */
{ 101, 38, "digital_vndr/pc(pc104)", KBD_ITALIAN }, /* Italy_x86 */
{ 101, 39, "digital_vndr/pc(pc104)", KBD_DUTCH }, /* Netherland_x86 */
{ 101, 40, "digital_vndr/pc(pc104)", KBD_NORWEGIAN }, /* Norway_x86 */
{ 101, 41, "digital_vndr/pc(pc104)", KBD_PORTUGUESE }, /* Portugal_x86 */
{ 101, 42, "digital_vndr/pc(pc104)", KBD_SPANISH }, /* Spain_x86 */
{ 101, 43, "digital_vndr/pc(pc104)", KBD_SWEDISH }, /* Sweden_x86 */
{ 101, 44, "digital_vndr/pc(pc104)", KBD_SWISS_FRENCH }, /* Switzer_Fr_x86 */
{ 101, 45, "digital_vndr/pc(pc104)", KBD_SWISS_GERMAN }, /* Switzer_Ge_x86 */
{ 101, 46, "digital_vndr/pc(pc104)", KBD_UNITED_KINGDOM }, /* UK_x86 */
{ 101, 47, "digital_vndr/pc(pc104)", KBD_KOREAN_INPUT_SYSTEM_IME_2000 }, /* Korea_x86 */
{ 101, 48, "digital_vndr/pc(pc104)", KBD_CHINESE_TRADITIONAL_PHONETIC }, /* Taiwan_x86 */
{ 101, 49, "digital_vndr/pc(lk411jj)", KBD_JAPANESE_INPUT_SYSTEM_MS_IME2002 }, /* Japan_x86 */
{ 101, 50, "digital_vndr/pc(pc104)", KBD_CANADIAN_FRENCH }, /* Canada_Fr2_x86 */
{ 101, 51, "digital_vndr/pc(pc104)", KBD_HUNGARIAN }, /* Hungary_x86 */
{ 101, 52, "digital_vndr/pc(pc104)", KBD_POLISH_214 }, /* Poland_x86 */
{ 101, 53, "digital_vndr/pc(pc104)", KBD_CZECH }, /* Czech_x86 */
{ 101, 54, "digital_vndr/pc(pc104)", KBD_RUSSIAN }, /* Russia_x86 */
{ 101, 55, "digital_vndr/pc(pc104)", KBD_LATVIAN }, /* Latvia_x86 */
{ 101, 56, "digital_vndr/pc(pc104)", KBD_TURKISH_Q }, /* Turkey_x86 */
{ 101, 57, "digital_vndr/pc(pc104)", KBD_GREEK }, /* Greece_x86 */
{ 101, 59, "digital_vndr/pc(pc104)", KBD_LITHUANIAN }, /* Lithuania_x86 */
{ 101, 1001, "digital_vndr/pc(pc104)", KBD_US }, /* MS_US101A_x86 */
{ 6, 6, "sun(type6tuv)", KBD_DANISH }, /* Denmark6_usb */
{ 6, 7, "sun(type6tuv)", KBD_FINNISH }, /* Finnish6_usb */
{ 6, 8, "sun(type6tuv)", KBD_FRENCH }, /* France6_usb */
{ 6, 9, "sun(type6tuv)", KBD_GERMAN }, /* Germany6_usb */
{ 6, 14, "sun(type6tuv)", KBD_ITALIAN }, /* Italy6_usb */
{ 6, 15, "sun(type6jp)", KBD_JAPANESE_INPUT_SYSTEM_MS_IME2002 }, /* Japan7_usb */
{ 6, 16, "sun(type6)", KBD_KOREAN_INPUT_SYSTEM_IME_2000 }, /* Korea6_usb */
{ 6, 18, "sun(type6tuv)", KBD_DUTCH }, /* Netherland6_usb */
{ 6, 19, "sun(type6tuv)", KBD_NORWEGIAN }, /* Norway6_usb */
{ 6, 22, "sun(type6tuv)", KBD_PORTUGUESE }, /* Portugal6_usb */
{ 6, 23, "sun(type6tuv)", KBD_RUSSIAN }, /* Russia6_usb */
{ 6, 25, "sun(type6tuv)", KBD_SPANISH }, /* Spain6_usb */
{ 6, 26, "sun(type6tuv)", KBD_SWEDISH }, /* Sweden6_usb */
{ 6, 27, "sun(type6tuv)", KBD_SWISS_FRENCH }, /* Switzer_Fr6_usb */
{ 6, 28, "sun(type6tuv)", KBD_SWISS_GERMAN }, /* Switzer_Ge6_usb */
{ 6, 30, "sun(type6)", KBD_CHINESE_TRADITIONAL_PHONETIC }, /* Taiwan6_usb */
{ 6, 32, "sun(type6tuv)", KBD_UNITED_KINGDOM }, /* UK6_usb */
{ 6, 33, "sun(type6)", KBD_US }, /* US6_usb */
{ 6, 1, "sun(type6tuv)", KBD_ARABIC_101 }, /* Arabic6_usb */
{ 6, 2, "sun(type6tuv)", KBD_BELGIAN_FRENCH }, /* Belgian6_usb */
{ 6, 31, "sun(type6tuv)", KBD_TURKISH_Q }, /* TurkeyQ6_usb */
{ 6, 35, "sun(type6tuv)", KBD_TURKISH_F }, /* TurkeyF6_usb */
{ 6, 271, "sun(type6jp)", KBD_JAPANESE_INPUT_SYSTEM_MS_IME2002 }, /* Japan6_usb */
{ 6, 264, "sun(type6tuv)", KBD_ALBANIAN }, /* Albanian6_usb */
{ 6, 261, "sun(type6tuv)", KBD_BELARUSIAN }, /* Belarusian6_usb */
{ 6, 260, "sun(type6tuv)", KBD_BULGARIAN }, /* Bulgarian6_usb */
{ 6, 259, "sun(type6tuv)", KBD_CROATIAN }, /* Croatian6_usb */
{ 6, 5, "sun(type6tuv)", KBD_CZECH }, /* Czech6_usb */
{ 6, 4, "sun(type6tuv)", KBD_CANADIAN_FRENCH }, /* French-Canadian6_usb */
{ 6, 12, "sun(type6tuv)", KBD_HUNGARIAN }, /* Hungarian6_usb */
{ 6, 10, "sun(type6tuv)", KBD_GREEK }, /* Greek6_usb */
{ 6, 17, "sun(type6)", KBD_LATIN_AMERICAN }, /* Latin-American6_usb */
{ 6, 265, "sun(type6tuv)", KBD_LITHUANIAN }, /* Lithuanian6_usb */
{ 6, 266, "sun(type6tuv)", KBD_LATVIAN }, /* Latvian6_usb */
{ 6, 267, "sun(type6tuv)", KBD_FYRO_MACEDONIAN }, /* Macedonian6_usb */
{ 6, 263, "sun(type6tuv)", KBD_MALTESE_47_KEY }, /* Malta_UK6_usb */
{ 6, 262, "sun(type6tuv)", KBD_MALTESE_48_KEY }, /* Malta_US6_usb */
{ 6, 21, "sun(type6tuv)", KBD_POLISH_214 }, /* Polish6_usb */
{ 6, 257, "sun(type6tuv)", KBD_SERBIAN_LATIN }, /* Serbia-And-Montenegro6_usb */
{ 6, 256, "sun(type6tuv)", KBD_SLOVENIAN }, /* Slovenian6_usb */
{ 6, 24, "sun(type6tuv)", KBD_SLOVAK }, /* Slovakian6_usb */
{ 6, 3, "sun(type6)", KBD_CANADIAN_MULTILINGUAL_STANDARD }, /* Canada_Bi6_usb */
{ 6, 272, "sun(type6)", KBD_PORTUGUESE_BRAZILIAN_ABNT } /* Brazil6_usb */
};
int freerdp_get_solaris_keyboard_layout_and_type(int* type, int* layout)
{
FILE* kbd;
char* pch;
char* beg;
char* end;
int rc = -1;
char buffer[1024];
/*
Sample output for "kbd -t -l" :
USB keyboard
type=6
layout=3 (0x03)
delay(ms)=500
rate(ms)=40
*/
*type = 0;
*layout = 0;
kbd = popen("kbd -t -l", "r");
if (!kbd)
return -1;
while (fgets(buffer, sizeof(buffer), kbd) != nullptr)
{
long val;
if ((pch = strstr(buffer, "type=")) != nullptr)
{
beg = pch + sizeof("type=") - 1;
end = strchr(beg, '\n');
end[0] = '\0';
errno = 0;
val = strtol(beg, nullptr, 0);
if ((errno != 0) || (val < INT32_MIN) || (val > INT32_MAX))
goto fail;
*type = val;
}
else if ((pch = strstr(buffer, "layout=")) != nullptr)
{
beg = pch + sizeof("layout=") - 1;
end = strchr(beg, ' ');
end[0] = '\0';
errno = 0;
val = strtol(beg, nullptr, 0);
if ((errno != 0) || (val < INT32_MIN) || (val > INT32_MAX))
goto fail;
*layout = val;
}
}
rc = 0;
fail:
pclose(kbd);
return rc;
}
DWORD freerdp_detect_solaris_keyboard_layout()
{
int type;
int layout;
if (freerdp_get_solaris_keyboard_layout_and_type(&type, &layout) < 0)
return 0;
for (size_t i = 0; i < ARRAYSIZE(SOLARIS_KEYBOARD_TABLE); i++)
{
if (SOLARIS_KEYBOARD_TABLE[i].type == type)
{
if (SOLARIS_KEYBOARD_TABLE[i].layout == layout)
return SOLARIS_KEYBOARD_TABLE[i].keyboardLayoutId;
}
}
return 0;
}

View File

@@ -0,0 +1,28 @@
/**
* FreeRDP: A Remote Desktop Protocol Implementation
* Solaris Keyboard Mapping
*
* Copyright 2009-2012 Marc-Andre Moreau <marcandre.moreau@gmail.com>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef FREERDP_LOCALE_KEYBOARD_SUN_H
#define FREERDP_LOCALE_KEYBOARD_SUN_H
#include <freerdp/api.h>
WINPR_ATTR_NODISCARD
FREERDP_LOCAL DWORD freerdp_detect_solaris_keyboard_layout();
#endif /* FREERDP_LOCALE_KEYBOARD_SUN_H */

View File

@@ -0,0 +1,144 @@
/**
* FreeRDP: A Remote Desktop Protocol Implementation
* X11 Keyboard Mapping
*
* Copyright 2009-2012 Marc-Andre Moreau <marcandre.moreau@gmail.com>
* Copyright 2023 Bernhard Miklautz <bernhard.miklautz@thincast.com>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <string.h>
#include <X11/X.h>
#include <X11/Xatom.h>
#include <X11/Xlib.h>
#include "liblocale.h"
#include "keyboard_x11.h"
#include "xkb_layout_ids.h"
static BOOL parse_xkb_rule_names(char* xkb_rule, unsigned long num_bytes, char** layout,
char** variant)
{
/* Sample output for "Canadian Multilingual Standard"
*
* _XKB_RULES_NAMES_BACKUP(STRING) = "xorg", "pc105", "ca", "multi", "magic"
*
* Format: "rules", "model", "layout", "variant", "options"
*
* Where "xorg" is the set of rules
* "pc105" the keyboard model
* "ca" the keyboard layout(s) (can also be something like 'us,uk')
* "multi" the keyboard layout variant(s) (in the examples, “,winkeys” - which means first
* layout uses some “default” variant and second uses “winkeys” variant)
* "magic" - configuration option (in the examples,
* “eurosign:e,lv3:ralt_switch,grp:rctrl_toggle”
* - three options)
*/
for (size_t i = 0, index = 0; i < num_bytes; i++, index++)
{
char* ptr = xkb_rule + i;
i += strnlen(ptr, num_bytes - i);
switch (index)
{
case 0: // rules
break;
case 1: // model
break;
case 2: // layout
{
/* If multiple languages are present we just take the first one */
char* delimiter = strchr(ptr, ',');
if (delimiter)
*delimiter = '\0';
*layout = ptr;
break;
}
case 3: // variant
{
/* If multiple variants are present we just take the first one */
char* delimiter = strchr(ptr, ',');
if (delimiter)
*delimiter = '\0';
*variant = ptr;
}
break;
case 4: // option
break;
default:
break;
}
}
return TRUE;
}
static DWORD kbd_layout_id_from_x_property(Display* display, Window root, char* property_name)
{
char* layout = nullptr;
char* variant = nullptr;
char* rule = nullptr;
Atom type = None;
int item_size = 0;
unsigned long items = 0;
unsigned long unread_items = 0;
DWORD layout_id = 0;
Atom property = XInternAtom(display, property_name, False);
if (property == None)
return 0;
if (XGetWindowProperty(display, root, property, 0, 1024, False, XA_STRING, &type, &item_size,
&items, &unread_items, (unsigned char**)&rule) != Success)
return 0;
if (type != XA_STRING || item_size != 8 || unread_items != 0)
{
XFree(rule);
return 0;
}
parse_xkb_rule_names(rule, items, &layout, &variant);
DEBUG_KBD("%s layout: %s, variant: %s", property_name, layout, variant);
layout_id = find_keyboard_layout_in_xorg_rules(layout, variant);
XFree(rule);
return layout_id;
}
int freerdp_detect_keyboard_layout_from_xkb(DWORD* keyboardLayoutId)
{
Display* display = XOpenDisplay(nullptr);
if (!display)
return 0;
Window root = DefaultRootWindow(display);
if (!root)
return 0;
/* We start by looking for _XKB_RULES_NAMES_BACKUP which appears to be used by libxklavier */
DWORD id = kbd_layout_id_from_x_property(display, root, "_XKB_RULES_NAMES_BACKUP");
if (0 == id)
id = kbd_layout_id_from_x_property(display, root, "_XKB_RULES_NAMES");
if (0 != id)
*keyboardLayoutId = id;
XCloseDisplay(display);
return (int)id;
}

View File

@@ -0,0 +1,29 @@
/**
* FreeRDP: A Remote Desktop Protocol Implementation
* X11 Keyboard Mapping
*
* Copyright 2009-2012 Marc-Andre Moreau <marcandre.moreau@gmail.com>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef FREERDP_LOCALE_KEYBOARD_X11_H
#define FREERDP_LOCALE_KEYBOARD_X11_H
#include <freerdp/api.h>
#if !defined(WITHOUT_FREERDP_3x_DEPRECATED)
FREERDP_LOCAL int freerdp_detect_keyboard_layout_from_xkb(DWORD* keyboardLayoutId);
#endif
#endif /* FREERDP_LOCALE_KEYBOARD_X11_H */

View File

@@ -0,0 +1,511 @@
/**
* FreeRDP: A Remote Desktop Protocol Implementation
* XKB Keyboard Mapping
*
* Copyright 2009-2012 Marc-Andre Moreau <marcandre.moreau@gmail.com>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <freerdp/config.h>
#include "keyboard_xkbfile.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <winpr/crt.h>
#include <winpr/input.h>
#include <freerdp/locale/keyboard.h>
#include "keyboard_x11.h"
#include "xkb_layout_ids.h"
#include "liblocale.h"
#include <X11/X.h>
#include <X11/Xlib.h>
#include <X11/XKBlib.h>
#include <X11/extensions/XKBfile.h>
#include <X11/extensions/XKBrules.h>
typedef struct
{
const char* xkb_keyname; /* XKB keyname */
DWORD rdp_scancode;
} XKB_KEY_NAME_SCANCODE;
static const XKB_KEY_NAME_SCANCODE XKB_KEY_NAME_SCANCODE_TABLE[] = {
{ "", RDP_SCANCODE_UNKNOWN }, /* 008: [(null)] */
{ "ESC", RDP_SCANCODE_ESCAPE }, /* 009: ESC [Escape] */
{ "AE01", RDP_SCANCODE_KEY_1 }, /* 010: AE01 [1] */
{ "AE02", RDP_SCANCODE_KEY_2 }, /* 011: AE02 [2] */
{ "AE03", RDP_SCANCODE_KEY_3 }, /* 012: AE03 [3] */
{ "AE04", RDP_SCANCODE_KEY_4 }, /* 013: AE04 [4] */
{ "AE05", RDP_SCANCODE_KEY_5 }, /* 014: AE05 [5] */
{ "AE06", RDP_SCANCODE_KEY_6 }, /* 015: AE06 [6] */
{ "AE07", RDP_SCANCODE_KEY_7 }, /* 016: AE07 [7] */
{ "AE08", RDP_SCANCODE_KEY_8 }, /* 017: AE08 [8] */
{ "AE09", RDP_SCANCODE_KEY_9 }, /* 018: AE09 [9] */
{ "AE10", RDP_SCANCODE_KEY_0 }, /* 019: AE10 [0] */
{ "AE11", RDP_SCANCODE_OEM_MINUS }, /* 020: AE11 [minus] */
{ "AE12", RDP_SCANCODE_OEM_PLUS }, /* 021: AE12 [equal] */
{ "BKSP", RDP_SCANCODE_BACKSPACE }, /* 022: BKSP [BackSpace] */
{ "TAB", RDP_SCANCODE_TAB }, /* 023: TAB [Tab] */
{ "AD01", RDP_SCANCODE_KEY_Q }, /* 024: AD01 [q] */
{ "AD02", RDP_SCANCODE_KEY_W }, /* 025: AD02 [w] */
{ "AD03", RDP_SCANCODE_KEY_E }, /* 026: AD03 [e] */
{ "AD04", RDP_SCANCODE_KEY_R }, /* 027: AD04 [r] */
{ "AD05", RDP_SCANCODE_KEY_T }, /* 028: AD05 [t] */
{ "AD06", RDP_SCANCODE_KEY_Y }, /* 029: AD06 [y] */
{ "AD07", RDP_SCANCODE_KEY_U }, /* 030: AD07 [u] */
{ "AD08", RDP_SCANCODE_KEY_I }, /* 031: AD08 [i] */
{ "AD09", RDP_SCANCODE_KEY_O }, /* 032: AD09 [o] */
{ "AD10", RDP_SCANCODE_KEY_P }, /* 033: AD10 [p] */
{ "AD11", RDP_SCANCODE_OEM_4 }, /* 034: AD11 [bracketleft] */
{ "AD12", RDP_SCANCODE_OEM_6 }, /* 035: AD12 [bracketright] */
{ "RTRN", RDP_SCANCODE_RETURN }, /* 036: RTRN [Return] */
{ "LCTL", RDP_SCANCODE_LCONTROL }, /* 037: LCTL [Control_L] */
{ "AC01", RDP_SCANCODE_KEY_A }, /* 038: AC01 [a] */
{ "AC02", RDP_SCANCODE_KEY_S }, /* 039: AC02 [s] */
{ "AC03", RDP_SCANCODE_KEY_D }, /* 040: AC03 [d] */
{ "AC04", RDP_SCANCODE_KEY_F }, /* 041: AC04 [f] */
{ "AC05", RDP_SCANCODE_KEY_G }, /* 042: AC05 [g] */
{ "AC06", RDP_SCANCODE_KEY_H }, /* 043: AC06 [h] */
{ "AC07", RDP_SCANCODE_KEY_J }, /* 044: AC07 [j] */
{ "AC08", RDP_SCANCODE_KEY_K }, /* 045: AC08 [k] */
{ "AC09", RDP_SCANCODE_KEY_L }, /* 046: AC09 [l] */
{ "AC10", RDP_SCANCODE_OEM_1 }, /* 047: AC10 [semicolon] */
{ "AC11", RDP_SCANCODE_OEM_7 }, /* 048: AC11 [dead_acute] */
{ "TLDE", RDP_SCANCODE_OEM_3 }, /* 049: TLDE [dead_grave] */
{ "LFSH", RDP_SCANCODE_LSHIFT }, /* 050: LFSH [Shift_L] */
{ "BKSL", RDP_SCANCODE_OEM_5 }, /* 051: BKSL [backslash] */
{ "AB01", RDP_SCANCODE_KEY_Z }, /* 052: AB01 [z] */
{ "AB02", RDP_SCANCODE_KEY_X }, /* 053: AB02 [x] */
{ "AB03", RDP_SCANCODE_KEY_C }, /* 054: AB03 [c] */
{ "AB04", RDP_SCANCODE_KEY_V }, /* 055: AB04 [v] */
{ "AB05", RDP_SCANCODE_KEY_B }, /* 056: AB05 [b] */
{ "AB06", RDP_SCANCODE_KEY_N }, /* 057: AB06 [n] */
{ "AB07", RDP_SCANCODE_KEY_M }, /* 058: AB07 [m] */
{ "AB08", RDP_SCANCODE_OEM_COMMA }, /* 059: AB08 [comma] */
{ "AB09", RDP_SCANCODE_OEM_PERIOD }, /* 060: AB09 [period] */
{ "AB10", RDP_SCANCODE_OEM_2 }, /* 061: AB10 [slash] */
{ "RTSH", RDP_SCANCODE_RSHIFT }, /* 062: RTSH [Shift_R] */
{ "KPMU", RDP_SCANCODE_MULTIPLY }, /* 063: KPMU [KP_Multiply] */
{ "LALT", RDP_SCANCODE_LMENU }, /* 064: LALT [Alt_L] */
{ "SPCE", RDP_SCANCODE_SPACE }, /* 065: SPCE [space] */
{ "CAPS", RDP_SCANCODE_CAPSLOCK }, /* 066: CAPS [Caps_Lock] */
{ "FK01", RDP_SCANCODE_F1 }, /* 067: FK01 [F1] */
{ "FK02", RDP_SCANCODE_F2 }, /* 068: FK02 [F2] */
{ "FK03", RDP_SCANCODE_F3 }, /* 069: FK03 [F3] */
{ "FK04", RDP_SCANCODE_F4 }, /* 070: FK04 [F4] */
{ "FK05", RDP_SCANCODE_F5 }, /* 071: FK05 [F5] */
{ "FK06", RDP_SCANCODE_F6 }, /* 072: FK06 [F6] */
{ "FK07", RDP_SCANCODE_F7 }, /* 073: FK07 [F7] */
{ "FK08", RDP_SCANCODE_F8 }, /* 074: FK08 [F8] */
{ "FK09", RDP_SCANCODE_F9 }, /* 075: FK09 [F9] */
{ "FK10", RDP_SCANCODE_F10 }, /* 076: FK10 [F10] */
{ "NMLK", RDP_SCANCODE_NUMLOCK }, /* 077: NMLK [Num_Lock] */
{ "SCLK", RDP_SCANCODE_SCROLLLOCK }, /* 078: SCLK [Multi_key] */
{ "KP7", RDP_SCANCODE_NUMPAD7 }, /* 079: KP7 [KP_Home] */
{ "KP8", RDP_SCANCODE_NUMPAD8 }, /* 080: KP8 [KP_Up] */
{ "KP9", RDP_SCANCODE_NUMPAD9 }, /* 081: KP9 [KP_Prior] */
{ "KPSU", RDP_SCANCODE_SUBTRACT }, /* 082: KPSU [KP_Subtract] */
{ "KP4", RDP_SCANCODE_NUMPAD4 }, /* 083: KP4 [KP_Left] */
{ "KP5", RDP_SCANCODE_NUMPAD5 }, /* 084: KP5 [KP_Begin] */
{ "KP6", RDP_SCANCODE_NUMPAD6 }, /* 085: KP6 [KP_Right] */
{ "KPAD", RDP_SCANCODE_ADD }, /* 086: KPAD [KP_Add] */
{ "KP1", RDP_SCANCODE_NUMPAD1 }, /* 087: KP1 [KP_End] */
{ "KP2", RDP_SCANCODE_NUMPAD2 }, /* 088: KP2 [KP_Down] */
{ "KP3", RDP_SCANCODE_NUMPAD3 }, /* 089: KP3 [KP_Next] */
{ "KP0", RDP_SCANCODE_NUMPAD0 }, /* 090: KP0 [KP_Insert] */
{ "KPDL", RDP_SCANCODE_DECIMAL }, /* 091: KPDL [KP_Delete] */
{ "LVL3", RDP_SCANCODE_RMENU }, /* 092: LVL3 [ISO_Level3_Shift] */
{ "", RDP_SCANCODE_UNKNOWN }, /* 093: [(null)] */
{ "LSGT", RDP_SCANCODE_OEM_102 }, /* 094: LSGT [backslash] */
{ "FK11", RDP_SCANCODE_F11 }, /* 095: FK11 [F11] */
{ "FK12", RDP_SCANCODE_F12 }, /* 096: FK12 [F12] */
{ "AB11", RDP_SCANCODE_ABNT_C1 }, /* 097: AB11 [(null)] */
{ "KATA", RDP_SCANCODE_KANA_HANGUL }, /* 098: KATA [Katakana] */
{ "HIRA", RDP_SCANCODE_HIRAGANA }, /* 099: HIRA [Hiragana] */
{ "HENK", RDP_SCANCODE_CONVERT_JP }, /* 100: HENK [Henkan_Mode] */
{ "HKTG", RDP_SCANCODE_HIRAGANA }, /* 101: HKTG [Hiragana_Katakana] */
{ "MUHE", RDP_SCANCODE_NONCONVERT_JP }, /* 102: MUHE [Muhenkan] */
{ "JPCM", RDP_SCANCODE_UNKNOWN }, /* 103: JPCM [(null)] */
{ "KPEN", RDP_SCANCODE_RETURN_KP }, /* 104: KPEN [KP_Enter] */
{ "RCTL", RDP_SCANCODE_RCONTROL }, /* 105: RCTL [Control_R] */
{ "KPDV", RDP_SCANCODE_DIVIDE }, /* 106: KPDV [KP_Divide] */
{ "PRSC", RDP_SCANCODE_PRINTSCREEN }, /* 107: PRSC [Print] */
{ "RALT", RDP_SCANCODE_RMENU }, /* 108: RALT [ISO_Level3_Shift] */
{ "LNFD", RDP_SCANCODE_UNKNOWN }, /* 109: LNFD [Linefeed] */
{ "HOME", RDP_SCANCODE_HOME }, /* 110: HOME [Home] */
{ "UP", RDP_SCANCODE_UP }, /* 111: UP [Up] */
{ "PGUP", RDP_SCANCODE_PRIOR }, /* 112: PGUP [Prior] */
{ "LEFT", RDP_SCANCODE_LEFT }, /* 113: LEFT [Left] */
{ "RGHT", RDP_SCANCODE_RIGHT }, /* 114: RGHT [Right] */
{ "END", RDP_SCANCODE_END }, /* 115: END [End] */
{ "DOWN", RDP_SCANCODE_DOWN }, /* 116: DOWN [Down] */
{ "PGDN", RDP_SCANCODE_NEXT }, /* 117: PGDN [Next] */
{ "INS", RDP_SCANCODE_INSERT }, /* 118: INS [Insert] */
{ "DELE", RDP_SCANCODE_DELETE }, /* 119: DELE [Delete] */
{ "I120", RDP_SCANCODE_UNKNOWN }, /* 120: I120 [(null)] */
{ "MUTE", RDP_SCANCODE_VOLUME_MUTE }, /* 121: MUTE [XF86AudioMute] */
{ "VOL-", RDP_SCANCODE_VOLUME_DOWN }, /* 122: VOL- [XF86AudioLowerVolume] */
{ "VOL+", RDP_SCANCODE_VOLUME_UP }, /* 123: VOL+ [XF86AudioRaiseVolume] */
{ "POWR", RDP_SCANCODE_UNKNOWN }, /* 124: POWR [XF86PowerOff] */
{ "KPEQ", RDP_SCANCODE_UNKNOWN }, /* 125: KPEQ [KP_Equal] */
{ "I126", RDP_SCANCODE_UNKNOWN }, /* 126: I126 [plusminus] */
{ "PAUS", RDP_SCANCODE_PAUSE }, /* 127: PAUS [Pause] */
{ "I128", RDP_SCANCODE_LAUNCH_MEDIA_SELECT }, /* 128: I128 [XF86LaunchA] */
{ "I129", RDP_SCANCODE_ABNT_C2 }, /* 129: I129 [KP_Decimal] */
{ "HNGL", RDP_SCANCODE_HANGUL }, /* 130: HNGL [Hangul] */
{ "HJCV", RDP_SCANCODE_HANJA }, /* 131: HJCV [Hangul_Hanja] */
{ "AE13", RDP_SCANCODE_BACKSLASH_JP }, /* 132: AE13 [(null)] */
{ "LWIN", RDP_SCANCODE_LWIN }, /* 133: LWIN [Super_L] */
{ "RWIN", RDP_SCANCODE_RWIN }, /* 134: RWIN [Super_R] */
{ "COMP", RDP_SCANCODE_APPS }, /* 135: COMP [Menu] */
{ "STOP", RDP_SCANCODE_BROWSER_STOP }, /* 136: STOP [Cancel] */
{ "AGAI" /* codespell:ignore */, RDP_SCANCODE_UNKNOWN },
/* 137: AGAI [Redo] */ /* codespell:ignore */
{ "PROP", RDP_SCANCODE_UNKNOWN }, /* 138: PROP [SunProps] */
{ "UNDO", RDP_SCANCODE_UNKNOWN }, /* 139: UNDO [Undo] */
{ "FRNT", RDP_SCANCODE_UNKNOWN }, /* 140: FRNT [SunFront] */
{ "COPY", RDP_SCANCODE_UNKNOWN }, /* 141: COPY [XF86Copy] */
{ "OPEN", RDP_SCANCODE_UNKNOWN }, /* 142: OPEN [XF86Open] */
{ "PAST", RDP_SCANCODE_UNKNOWN }, /* 143: PAST [XF86Paste] */
{ "FIND", RDP_SCANCODE_UNKNOWN }, /* 144: FIND [Find] */
{ "CUT", RDP_SCANCODE_UNKNOWN }, /* 145: CUT [XF86Cut] */
{ "HELP", RDP_SCANCODE_HELP }, /* 146: HELP [Help] */
{ "I147", RDP_SCANCODE_UNKNOWN }, /* 147: I147 [XF86MenuKB] */
{ "I148", RDP_SCANCODE_UNKNOWN }, /* 148: I148 [XF86Calculator] */
{ "I149", RDP_SCANCODE_UNKNOWN }, /* 149: I149 [(null)] */
{ "I150", RDP_SCANCODE_SLEEP }, /* 150: I150 [XF86Sleep] */
{ "I151", RDP_SCANCODE_UNKNOWN }, /* 151: I151 [XF86WakeUp] */
{ "I152", RDP_SCANCODE_UNKNOWN }, /* 152: I152 [XF86Explorer] */
{ "I153", RDP_SCANCODE_UNKNOWN }, /* 153: I153 [XF86Send] */
{ "I154", RDP_SCANCODE_UNKNOWN }, /* 154: I154 [(null)] */
{ "I155", RDP_SCANCODE_UNKNOWN }, /* 155: I155 [XF86Xfer] */
{ "I156", RDP_SCANCODE_LAUNCH_APP1 }, /* 156: I156 [XF86Launch1] */
{ "I157", RDP_SCANCODE_LAUNCH_APP2 }, /* 157: I157 [XF86Launch2] */
{ "I158", RDP_SCANCODE_BROWSER_HOME }, /* 158: I158 [XF86WWW] */
{ "I159", RDP_SCANCODE_UNKNOWN }, /* 159: I159 [XF86DOS] */
{ "I160", RDP_SCANCODE_UNKNOWN }, /* 160: I160 [XF86ScreenSaver] */
{ "I161", RDP_SCANCODE_UNKNOWN }, /* 161: I161 [XF86RotateWindows] */
{ "I162", RDP_SCANCODE_UNKNOWN }, /* 162: I162 [XF86TaskPane] */
{ "I163", RDP_SCANCODE_LAUNCH_MAIL }, /* 163: I163 [XF86Mail] */
{ "I164", RDP_SCANCODE_BROWSER_FAVORITES }, /* 164: I164 [XF86Favorites] */
{ "I165", RDP_SCANCODE_UNKNOWN }, /* 165: I165 [XF86MyComputer] */
{ "I166", RDP_SCANCODE_BROWSER_BACK }, /* 166: I166 [XF86Back] */
{ "I167", RDP_SCANCODE_BROWSER_FORWARD }, /* 167: I167 [XF86Forward] */
{ "I168", RDP_SCANCODE_UNKNOWN }, /* 168: I168 [(null)] */
{ "I169", RDP_SCANCODE_UNKNOWN }, /* 169: I169 [XF86Eject] */
{ "I170", RDP_SCANCODE_UNKNOWN }, /* 170: I170 [XF86Eject] */
{ "I171", RDP_SCANCODE_MEDIA_NEXT_TRACK }, /* 171: I171 [XF86AudioNext] */
{ "I172", RDP_SCANCODE_MEDIA_PLAY_PAUSE }, /* 172: I172 [XF86AudioPlay] */
{ "I173", RDP_SCANCODE_MEDIA_PREV_TRACK }, /* 173: I173 [XF86AudioPrev] */
{ "I174", RDP_SCANCODE_MEDIA_STOP }, /* 174: I174 [XF86AudioStop] */
{ "I175", RDP_SCANCODE_UNKNOWN }, /* 175: I175 [XF86AudioRecord] */
{ "I176", RDP_SCANCODE_UNKNOWN }, /* 176: I176 [XF86AudioRewind] */
{ "I177", RDP_SCANCODE_UNKNOWN }, /* 177: I177 [XF86Phone] */
{ "I178", RDP_SCANCODE_UNKNOWN }, /* 178: I178 [(null)] */
{ "I179", RDP_SCANCODE_UNKNOWN }, /* 179: I179 [XF86Tools] */
{ "I180", RDP_SCANCODE_BROWSER_HOME }, /* 180: I180 [XF86HomePage] */
{ "I181", RDP_SCANCODE_BROWSER_REFRESH }, /* 181: I181 [XF86Reload] */
{ "I182", RDP_SCANCODE_UNKNOWN }, /* 182: I182 [XF86Close] */
{ "I183", RDP_SCANCODE_UNKNOWN }, /* 183: I183 [(null)] */
{ "I184", RDP_SCANCODE_UNKNOWN }, /* 184: I184 [(null)] */
{ "I185", RDP_SCANCODE_UNKNOWN }, /* 185: I185 [XF86ScrollUp] */
{ "I186", RDP_SCANCODE_UNKNOWN }, /* 186: I186 [XF86ScrollDown] */
{ "I187", RDP_SCANCODE_UNKNOWN }, /* 187: I187 [parenleft] */
{ "I188", RDP_SCANCODE_UNKNOWN }, /* 188: I188 [parenright] */
{ "I189", RDP_SCANCODE_UNKNOWN }, /* 189: I189 [XF86New] */
{ "I190", RDP_SCANCODE_UNKNOWN }, /* 190: I190 [Redo] */
{ "FK13", RDP_SCANCODE_F13 }, /* 191: FK13 [XF86Tools] */
{ "FK14", RDP_SCANCODE_F14 }, /* 192: FK14 [XF86Launch5] */
{ "FK15", RDP_SCANCODE_F15 }, /* 193: FK15 [XF86Launch6] */
{ "FK16", RDP_SCANCODE_F16 }, /* 194: FK16 [XF86Launch7] */
{ "FK17", RDP_SCANCODE_F17 }, /* 195: FK17 [XF86Launch8] */
{ "FK18", RDP_SCANCODE_F18 }, /* 196: FK18 [XF86Launch9] */
{ "FK19", RDP_SCANCODE_F19 }, /* 197: FK19 [(null)] */
{ "FK20", RDP_SCANCODE_F20 }, /* 198: FK20 [XF86AudioMicMute] */
{ "FK21", RDP_SCANCODE_F21 }, /* 199: FK21 [XF86TouchpadToggle] */
{ "FK22", RDP_SCANCODE_F22 }, /* 200: FK22 [XF86TouchpadOn] */
{ "FK23", RDP_SCANCODE_F23 }, /* 201: FK23 [XF86TouchpadOff] */
{ "FK24", RDP_SCANCODE_F24 }, /* 202: FK24 [(null)] */
{ "LVL5", RDP_SCANCODE_UNKNOWN }, /* 203: LVL5 [ISO_Level5_Shift] */
{ "ALT", RDP_SCANCODE_LMENU }, /* 204: ALT [(null)] */
{ "META", RDP_SCANCODE_LMENU }, /* 205: META [(null)] */
{ "SUPR", RDP_SCANCODE_LWIN }, /* 206: SUPR [(null)] */
{ "HYPR", RDP_SCANCODE_LWIN }, /* 207: HYPR [(null)] */
{ "I208", RDP_SCANCODE_MEDIA_PLAY_PAUSE }, /* 208: I208 [XF86AudioPlay] */
{ "I209", RDP_SCANCODE_MEDIA_PLAY_PAUSE }, /* 209: I209 [XF86AudioPause] */
{ "I210", RDP_SCANCODE_UNKNOWN }, /* 210: I210 [XF86Launch3] */
{ "I211", RDP_SCANCODE_UNKNOWN }, /* 211: I211 [XF86Launch4] */
{ "I212", RDP_SCANCODE_UNKNOWN }, /* 212: I212 [XF86LaunchB] */
{ "I213", RDP_SCANCODE_UNKNOWN }, /* 213: I213 [XF86Suspend] */
{ "I214", RDP_SCANCODE_UNKNOWN }, /* 214: I214 [XF86Close] */
{ "I215", RDP_SCANCODE_MEDIA_PLAY_PAUSE }, /* 215: I215 [XF86AudioPlay] */
{ "I216", RDP_SCANCODE_MEDIA_NEXT_TRACK }, /* 216: I216 [XF86AudioForward] */
{ "I217", RDP_SCANCODE_UNKNOWN }, /* 217: I217 [(null)] */
{ "I218", RDP_SCANCODE_UNKNOWN }, /* 218: I218 [Print] */
{ "I219", RDP_SCANCODE_UNKNOWN }, /* 219: I219 [(null)] */
{ "I220", RDP_SCANCODE_UNKNOWN }, /* 220: I220 [XF86WebCam] */
{ "I221", RDP_SCANCODE_UNKNOWN }, /* 221: I221 [XF86AudioPreset] */
{ "I222", RDP_SCANCODE_UNKNOWN }, /* 222: I222 [(null)] */
{ "I223", RDP_SCANCODE_LAUNCH_MAIL }, /* 223: I223 [XF86Mail] */
{ "I224", RDP_SCANCODE_UNKNOWN }, /* 224: I224 [XF86Messenger] */
{ "I225", RDP_SCANCODE_BROWSER_SEARCH }, /* 225: I225 [XF86Search] */
{ "I226", RDP_SCANCODE_UNKNOWN }, /* 226: I226 [XF86Go] */
{ "I227", RDP_SCANCODE_UNKNOWN }, /* 227: I227 [XF86Finance] */
{ "I228", RDP_SCANCODE_UNKNOWN }, /* 228: I228 [XF86Game] */
{ "I229", RDP_SCANCODE_UNKNOWN }, /* 229: I229 [XF86Shop] */
{ "I230", RDP_SCANCODE_UNKNOWN }, /* 230: I230 [(null)] */
{ "I231", RDP_SCANCODE_UNKNOWN }, /* 231: I231 [Cancel] */
{ "I232", RDP_SCANCODE_UNKNOWN }, /* 232: I232 [XF86MonBrightnessDown] */
{ "I233", RDP_SCANCODE_UNKNOWN }, /* 233: I233 [XF86MonBrightnessUp] */
{ "I234", RDP_SCANCODE_LAUNCH_MEDIA_SELECT }, /* 234: I234 [XF86AudioMedia] */
{ "I235", RDP_SCANCODE_UNKNOWN }, /* 235: I235 [XF86Display] */
{ "I236", RDP_SCANCODE_UNKNOWN }, /* 236: I236 [XF86KbdLightOnOff] */
{ "I237", RDP_SCANCODE_UNKNOWN }, /* 237: I237 [XF86KbdBrightnessDown] */
{ "I238", RDP_SCANCODE_UNKNOWN }, /* 238: I238 [XF86KbdBrightnessUp] */
{ "I239", RDP_SCANCODE_UNKNOWN }, /* 239: I239 [XF86Send] */
{ "I240", RDP_SCANCODE_UNKNOWN }, /* 240: I240 [XF86Reply] */
{ "I241", RDP_SCANCODE_UNKNOWN }, /* 241: I241 [XF86MailForward] */
{ "I242", RDP_SCANCODE_UNKNOWN }, /* 242: I242 [XF86Save] */
{ "I243", RDP_SCANCODE_UNKNOWN }, /* 243: I243 [XF86Documents] */
{ "I244", RDP_SCANCODE_UNKNOWN }, /* 244: I244 [XF86Battery] */
{ "I245", RDP_SCANCODE_UNKNOWN }, /* 245: I245 [XF86Bluetooth] */
{ "I246", RDP_SCANCODE_UNKNOWN }, /* 246: I246 [XF86WLAN] */
{ "I247", RDP_SCANCODE_UNKNOWN }, /* 247: I247 [XF86UWB] */
{ "I248", RDP_SCANCODE_UNKNOWN }, /* 248: I248 [(null)] */
{ "I249", RDP_SCANCODE_UNKNOWN }, /* 249: I249 [XF86Next_VMode] */
{ "I250", RDP_SCANCODE_UNKNOWN }, /* 250: I250 [XF86Prev_VMode] */
{ "I251", RDP_SCANCODE_UNKNOWN }, /* 251: I251 [XF86MonBrightnessCycle] */
{ "I252", RDP_SCANCODE_UNKNOWN }, /* 252: I252 [XF86BrightnessAuto] */
{ "I253", RDP_SCANCODE_UNKNOWN }, /* 253: I253 [XF86DisplayOff] */
{ "I254", RDP_SCANCODE_UNKNOWN }, /* 254: I254 [XF86WWAN] */
{ "I255", RDP_SCANCODE_UNKNOWN } /* 255: I255 [XF86RFKill] */
};
static int detect_keyboard_layout_from_xkbfile(void* display, DWORD* keyboardLayoutId);
static int freerdp_keyboard_load_map_from_xkbfile(void* display, DWORD* x11_keycode_to_rdp_scancode,
size_t count);
static void* freerdp_keyboard_xkb_init(void)
{
int status = 0;
Display* display = XOpenDisplay(nullptr);
if (!display)
return nullptr;
status = XkbQueryExtension(display, nullptr, nullptr, nullptr, nullptr, nullptr);
if (!status)
return nullptr;
return (void*)display;
}
int freerdp_keyboard_init_xkbfile(DWORD* keyboardLayoutId, DWORD* x11_keycode_to_rdp_scancode,
size_t count)
{
WINPR_ASSERT(keyboardLayoutId);
WINPR_ASSERT(x11_keycode_to_rdp_scancode);
ZeroMemory(x11_keycode_to_rdp_scancode, sizeof(DWORD) * count);
void* display = freerdp_keyboard_xkb_init();
if (!display)
{
DEBUG_KBD("Error initializing xkb");
return -1;
}
if (*keyboardLayoutId == 0)
{
detect_keyboard_layout_from_xkbfile(display, keyboardLayoutId);
DEBUG_KBD("detect_keyboard_layout_from_xkb: %" PRIu32 " (0x%08" PRIX32 ")",
*keyboardLayoutId, *keyboardLayoutId);
}
const int rc =
freerdp_keyboard_load_map_from_xkbfile(display, x11_keycode_to_rdp_scancode, count);
XCloseDisplay(display);
return rc;
}
/* return substring starting after nth comma, ending at following comma */
static char* comma_substring(char* s, size_t n)
{
char* p = nullptr;
if (!s)
return "";
while (n-- > 0)
{
if (!(p = strchr(s, ',')))
break;
s = p + 1;
}
if ((p = strchr(s, ',')))
*p = 0;
return s;
}
int detect_keyboard_layout_from_xkbfile(void* display, DWORD* keyboardLayoutId)
{
DEBUG_KBD("display: %p", display);
if (!display)
return -2;
char* rules = nullptr;
XkbRF_VarDefsRec rules_names = WINPR_C_ARRAY_INIT;
const Bool rc = XkbRF_GetNamesProp(display, &rules, &rules_names);
if (!rc)
{
DEBUG_KBD("XkbRF_GetNamesProp == False");
}
else
{
DEBUG_KBD("rules: %s", rules ? rules : "");
DEBUG_KBD("model: %s", rules_names.model ? rules_names.model : "");
DEBUG_KBD("layouts: %s", rules_names.layout ? rules_names.layout : "");
DEBUG_KBD("variants: %s", rules_names.variant ? rules_names.variant : "");
DWORD group = 0;
XkbStateRec state = WINPR_C_ARRAY_INIT;
XKeyboardState coreKbdState = WINPR_C_ARRAY_INIT;
XGetKeyboardControl(display, &coreKbdState);
if (XkbGetState(display, XkbUseCoreKbd, &state) == Success)
group = state.group;
DEBUG_KBD("group: %u", state.group);
const char* layout = comma_substring(rules_names.layout, group);
const char* variant = comma_substring(rules_names.variant, group);
DEBUG_KBD("layout: %s", layout ? layout : "");
DEBUG_KBD("variant: %s", variant ? variant : "");
*keyboardLayoutId = find_keyboard_layout_in_xorg_rules(layout, variant);
}
free(rules_names.model);
free(rules_names.layout);
free(rules_names.variant);
free(rules_names.options);
free(rules);
return 0;
}
static int xkb_cmp(const void* pva, const void* pvb)
{
const XKB_KEY_NAME_SCANCODE* a = pva;
const XKB_KEY_NAME_SCANCODE* b = pvb;
if (!a && !b)
return 0;
if (!a)
return 1;
if (!b)
return -1;
return strcmp(a->xkb_keyname, b->xkb_keyname);
}
static BOOL try_add(size_t offset, const char* xkb_keyname, DWORD* x11_keycode_to_rdp_scancode,
WINPR_ATTR_UNUSED size_t count)
{
static BOOL initialized = FALSE;
static XKB_KEY_NAME_SCANCODE copy[ARRAYSIZE(XKB_KEY_NAME_SCANCODE_TABLE)] = WINPR_C_ARRAY_INIT;
if (!initialized)
{
memcpy(copy, XKB_KEY_NAME_SCANCODE_TABLE, sizeof(copy));
qsort(copy, ARRAYSIZE(copy), sizeof(XKB_KEY_NAME_SCANCODE), xkb_cmp);
initialized = TRUE;
}
XKB_KEY_NAME_SCANCODE key = WINPR_C_ARRAY_INIT;
key.xkb_keyname = xkb_keyname;
XKB_KEY_NAME_SCANCODE* found =
bsearch(&key, copy, ARRAYSIZE(copy), sizeof(XKB_KEY_NAME_SCANCODE), xkb_cmp);
if (found)
{
DEBUG_KBD("%4s: keycode: 0x%02" PRIuz " -> rdp scancode: 0x%08" PRIx32 "", xkb_keyname,
offset, found->rdp_scancode);
x11_keycode_to_rdp_scancode[offset] = found->rdp_scancode;
return TRUE;
}
return FALSE;
}
int freerdp_keyboard_load_map_from_xkbfile(void* display, DWORD* x11_keycode_to_rdp_scancode,
size_t count)
{
int status = -1;
if (!display)
return -2;
XkbDescPtr xkb = XkbGetMap(display, 0, XkbUseCoreKbd);
if (!xkb)
{
DEBUG_KBD("XkbGetMap() == nullptr");
return -3;
}
if (XkbGetNames(display, XkbKeyNamesMask, xkb) != Success)
{
DEBUG_KBD("XkbGetNames() != Success");
}
else
{
char xkb_keyname[XkbKeyNameLength + 1] = { 42, 42, 42, 42,
0 }; /* end-of-string at index 5 */
DEBUG_KBD("XkbGetNames() == Success, min=%" PRIu8 ", max=%" PRIu8, xkb->min_key_code,
xkb->max_key_code);
for (size_t i = xkb->min_key_code; i <= MIN(xkb->max_key_code, count); i++)
{
BOOL found = FALSE;
strncpy(xkb_keyname, xkb->names->keys[i].name, XkbKeyNameLength);
DEBUG_KBD("KeyCode %" PRIuz " -> %s", i, xkb_keyname);
if (strnlen(xkb_keyname, ARRAYSIZE(xkb_keyname)) < 1)
continue;
found = try_add(i, xkb_keyname, x11_keycode_to_rdp_scancode, count);
if (!found)
{
DEBUG_KBD("%4s: keycode: 0x%02X -> no RDP scancode found", xkb_keyname, i);
}
else
status = 0;
}
}
XkbFreeKeyboard(xkb, 0, 1);
return status;
}

View File

@@ -0,0 +1,31 @@
/**
* FreeRDP: A Remote Desktop Protocol Implementation
* XKB Keyboard Mapping
*
* Copyright 2009-2012 Marc-Andre Moreau <marcandre.moreau@gmail.com>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef FREERDP_LIB_LOCALE_KEYBOARD_XKB_H
#define FREERDP_LIB_LOCALE_KEYBOARD_XKB_H
#include <freerdp/types.h>
#include <freerdp/locale/keyboard.h>
#include <freerdp/api.h>
WINPR_ATTR_NODISCARD
FREERDP_LOCAL int freerdp_keyboard_init_xkbfile(DWORD* keyboardLayoutId,
DWORD* x11_keycode_to_rdp_scancode, size_t count);
#endif /* FREERDP_LIB_LOCALE_KEYBOARD_XKB_H */

View File

@@ -0,0 +1,39 @@
/**
* FreeRDP: A Remote Desktop Protocol Implementation
* RDP Localization
*
* Copyright 2009-2012 Marc-Andre Moreau <marcandre.moreau@gmail.com>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef FREERDP_LIB_LOCALE_LIB_H
#define FREERDP_LIB_LOCALE_LIB_H
#include <freerdp/config.h>
#include <freerdp/log.h>
#define KBD_TAG FREERDP_TAG("locale")
#ifdef WITH_DEBUG_KBD
#define DEBUG_KBD(...) WLog_DBG(KBD_TAG, __VA_ARGS__)
#else
#define DEBUG_KBD(...) \
do \
{ \
} while (0)
#endif
#define TIMEZONE_TAG FREERDP_TAG("timezone")
#endif /* FREERDP_LIB_LOCALE_LIB_H */

View File

@@ -0,0 +1,894 @@
/**
* FreeRDP: A Remote Desktop Protocol Implementation
* Microsoft Locales
*
* Copyright 2009-2012 Marc-Andre Moreau <marcandre.moreau@gmail.com>
* Copyright 2021 Thincast Technologies GmbH
* Copyright 2021 Martin Fleisz <martin.fleisz@thincast.com>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <freerdp/config.h>
#if defined(__APPLE__)
#include <CoreFoundation/CFString.h>
#include <CoreFoundation/CFLocale.h>
#endif
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <winpr/crt.h>
#include <winpr/assert.h>
#include <winpr/environment.h>
#include "liblocale.h"
#include <freerdp/locale/locale.h>
/*
* Refer to MSDN article "Locale Identifier Constants and Strings":
* http://msdn.microsoft.com/en-us/library/ms776260.aspx
*/
static const SYSTEM_LOCALE SYSTEM_LOCALE_TABLE[] = {
{ "af", "ZA", AFRIKAANS }, /* Afrikaans (South Africa) */
{ "sq", "AL", ALBANIAN }, /* Albanian (Albania) */
{ "gsw", "FR", ALSATIAN }, /* Windows Vista and later: Alsatian (France) */
{ "am", "ET", AMHARIC }, /* Windows Vista and later: Amharic (Ethiopia) */
{ "ar", "DZ", ARABIC_ALGERIA }, /* Arabic (Algeria) */
{ "ar", "BH", ARABIC_BAHRAIN }, /* Arabic (Bahrain) */
{ "ar", "EG", ARABIC_EGYPT }, /* Arabic (Egypt) */
{ "ar", "IQ", ARABIC_IRAQ }, /* Arabic (Iraq) */
{ "ar", "JO", ARABIC_JORDAN }, /* Arabic (Jordan) */
{ "ar", "KW", ARABIC_KUWAIT }, /* Arabic (Kuwait) */
{ "ar", "LB", ARABIC_LEBANON }, /* Arabic (Lebanon) */
{ "ar", "LY", ARABIC_LIBYA }, /* Arabic (Libya) */
{ "ar", "MA", ARABIC_MOROCCO }, /* Arabic (Morocco) */
{ "ar", "OM", ARABIC_OMAN }, /* Arabic (Oman) */
{ "ar", "QA", ARABIC_QATAR }, /* Arabic (Qatar) */
{ "ar", "SA", ARABIC_SAUDI_ARABIA }, /* Arabic (Saudi Arabia) */
{ "ar", "SY", ARABIC_SYRIA }, /* Arabic (Syria) */
{ "ar", "TN", ARABIC_TUNISIA }, /* Arabic (Tunisia) */
{ "ar", "AE", ARABIC_UAE }, /* Arabic (U.A.E.) */
{ "ar", "YE", ARABIC_YEMEN }, /* Arabic (Yemen) */
{ "az", "AZ", AZERI_LATIN }, /* Azeri (Latin) */
{ "az", "Cyrl_AZ", AZERI_CYRILLIC }, /* Azeri (Cyrillic) */
{ "hy", "AM", ARMENIAN }, /* Windows 2000 and later: Armenian (Armenia) */
{ "as", "IN", ASSAMESE }, /* Windows Vista and later: Assamese (India) */
{ "ba", "RU", BASHKIR }, /* Windows Vista and later: Bashkir (Russia) */
{ "eu", "ES", BASQUE }, /* Basque (Basque) */
{ "be", "BY", BELARUSIAN }, /* Belarusian (Belarus) */
{ "bn", "IN", BENGALI_INDIA }, /* Windows XP SP2 and later: Bengali (India) */
{ "br", "FR", BRETON }, /* Breton (France) */
{ "bs", "BA", BOSNIAN_LATIN }, /* Bosnian (Latin) */
{ "bg", "BG", BULGARIAN }, /* Bulgarian (Bulgaria) */
{ "ca", "ES", CATALAN }, /* Catalan (Catalan) */
{ "zh", "HK", CHINESE_HONG_KONG }, /* Chinese (Hong Kong SAR, PRC) */
{ "zh", "MO", CHINESE_MACAU }, /* Windows 98/Me, Windows XP and later: Chinese (Macao SAR) */
{ "zh", "CN", CHINESE_PRC }, /* Chinese (PRC) */
{ "zh", "SG", CHINESE_SINGAPORE }, /* Chinese (Singapore) */
{ "zh", "TW", CHINESE_TAIWAN }, /* Chinese (Taiwan) */
{ "hr", "BA", CROATIAN_BOSNIA_HERZEGOVINA }, /* Windows XP SP2 and later: Croatian (Bosnia and
Herzegovina, Latin) */
{ "hr", "HR", CROATIAN }, /* Croatian (Croatia) */
{ "cs", "CZ", CZECH }, /* Czech (Czech Republic) */
{ "da", "DK", DANISH }, /* Danish (Denmark) */
{ "prs", "AF", DARI }, /* Windows XP and later: Dari (Afghanistan) */
{ "dv", "MV", DIVEHI }, /* Windows XP and later: Divehi (Maldives) */
{ "nl", "BE", DUTCH_BELGIAN }, /* Dutch (Belgium) */
{ "nl", "NL", DUTCH_STANDARD }, /* Dutch (Netherlands) */
{ "en", "AU", ENGLISH_AUSTRALIAN }, /* English (Australia) */
{ "en", "BZ", ENGLISH_BELIZE }, /* English (Belize) */
{ "en", "CA", ENGLISH_CANADIAN }, /* English (Canada) */
{ "en", "CB", ENGLISH_CARIBBEAN }, /* English (Caribbean) */
{ "en", "IN", ENGLISH_INDIA }, /* Windows Vista and later: English (India) */
{ "en", "IE", ENGLISH_IRELAND }, /* English (Ireland) */
{ "en", "JM", ENGLISH_JAMAICA }, /* English (Jamaica) */
{ "en", "MY", ENGLISH_MALAYSIA }, /* Windows Vista and later: English (Malaysia) */
{ "en", "NZ", ENGLISH_NEW_ZEALAND }, /* English (New Zealand) */
{ "en", "PH",
ENGLISH_PHILIPPINES }, /* Windows 98/Me, Windows 2000 and later: English (Philippines) */
{ "en", "SG", ENGLISH_SINGAPORE }, /* Windows Vista and later: English (Singapore) */
{ "en", "ZA", ENGLISH_SOUTH_AFRICA }, /* English (South Africa) */
{ "en", "TT", ENGLISH_TRINIDAD }, /* English (Trinidad and Tobago) */
{ "en", "GB", ENGLISH_UNITED_KINGDOM }, /* English (United Kingdom) */
{ "en", "US", ENGLISH_UNITED_STATES }, /* English (United States) */
{ "en", "ZW",
ENGLISH_ZIMBABWE }, /* Windows 98/Me, Windows 2000 and later: English (Zimbabwe) */
{ "et", "EE", ESTONIAN }, /* Estonian (Estonia) */
{ "fo", "FO", FAEROESE }, /* Faroese (Faroe Islands) */
{ "fil", "PH", FILIPINO }, /* Windows XP SP2 and later (downloadable); Windows Vista and later:
Filipino (Philippines) */
{ "fi", "FI", FINNISH }, /* Finnish (Finland) */
{ "fr", "BE", FRENCH_BELGIAN }, /* French (Belgium) */
{ "fr", "CA", FRENCH_CANADIAN }, /* French (Canada) */
{ "fr", "FR", FRENCH_STANDARD }, /* French (France) */
{ "fr", "LU", FRENCH_LUXEMBOURG }, /* French (Luxembourg) */
{ "fr", "MC", FRENCH_MONACO }, /* French (Monaco) */
{ "fr", "CH", FRENCH_SWISS }, /* French (Switzerland) */
{ "fy", "NL", FRISIAN }, /* Windows XP SP2 and later (downloadable); Windows Vista and later:
Frisian (Netherlands) */
{ "gl", "ES", GALICIAN }, /* Windows XP and later: Galician (Spain) */
{ "ka", "GE", GEORGIAN }, /* Windows 2000 and later: Georgian (Georgia) */
{ "de", "AT", GERMAN_AUSTRIAN }, /* German (Austria) */
{ "de", "DE", GERMAN_STANDARD }, /* German (Germany) */
{ "de", "LI", GERMAN_LIECHTENSTEIN }, /* German (Liechtenstein) */
{ "de", "LU", GERMAN_LUXEMBOURG }, /* German (Luxembourg) */
{ "de", "CH", GERMAN_SWISS }, /* German (Switzerland) */
{ "el", "GR", GREEK }, /* Greek (Greece) */
{ "kl", "GL", GREENLANDIC }, /* Windows Vista and later: Greenlandic (Greenland) */
{ "gu", "IN", GUJARATI }, /* Windows XP and later: Gujarati (India) */
{ "he", "IL", HEBREW }, /* Hebrew (Israel) */
{ "hi", "IN", HINDI }, /* Windows 2000 and later: Hindi (India) */
{ "hu", "HU", HUNGARIAN }, /* Hungarian (Hungary) */
{ "is", "IS", ICELANDIC }, /* Icelandic (Iceland) */
{ "ig", "NG", IGBO }, /* Igbo (Nigeria) */
{ "id", "ID", INDONESIAN }, /* Indonesian (Indonesia) */
{ "ga", "IE", IRISH }, /* Windows XP SP2 and later (downloadable); Windows Vista and later:
Irish (Ireland) */
{ "it", "IT", ITALIAN_STANDARD }, /* Italian (Italy) */
{ "it", "CH", ITALIAN_SWISS }, /* Italian (Switzerland) */
{ "ja", "JP", JAPANESE }, /* Japanese (Japan) */
{ "kn", "IN", KANNADA }, /* Windows XP and later: Kannada (India) */
{ "kk", "KZ", KAZAKH }, /* Windows 2000 and later: Kazakh (Kazakhstan) */
{ "kh", "KH", KHMER }, /* Windows Vista and later: Khmer (Cambodia) */
{ "qut", "GT", KICHE }, /* Windows Vista and later: K'iche (Guatemala) */
{ "rw", "RW", KINYARWANDA }, /* Windows Vista and later: Kinyarwanda (Rwanda) */
{ "kok", "IN", KONKANI }, /* Windows 2000 and later: Konkani (India) */
{ "ko", "KR", KOREAN }, /* Korean (Korea) */
{ "ky", "KG", KYRGYZ }, /* Windows XP and later: Kyrgyz (Kyrgyzstan) */
{ "lo", "LA", LAO }, /* Windows Vista and later: Lao (Lao PDR) */
{ "lv", "LV", LATVIAN }, /* Latvian (Latvia) */
{ "lt", "LT", LITHUANIAN }, /* Lithuanian (Lithuania) */
{ "dsb", "DE", LOWER_SORBIAN }, /* Windows Vista and later: Lower Sorbian (Germany) */
{ "lb", "LU", LUXEMBOURGISH }, /* Windows XP SP2 and later (downloadable); Windows Vista and
later: Luxembourgish (Luxembourg) */
{ "mk", "MK", MACEDONIAN }, /* Windows 2000 and later: Macedonian (Macedonia, FYROM) */
{ "ms", "BN", MALAY_BRUNEI_DARUSSALAM }, /* Windows 2000 and later: Malay (Brunei Darussalam) */
{ "ms", "MY", MALAY_MALAYSIA }, /* Windows 2000 and later: Malay (Malaysia) */
{ "ml", "IN", MALAYALAM }, /* Windows XP SP2 and later: Malayalam (India) */
{ "mt", "MT", MALTESE }, /* Windows XP SP2 and later: Maltese (Malta) */
{ "mi", "NZ", MAORI }, /* Windows XP SP2 and later: Maori (New Zealand) */
{ "arn", "CL", MAPUDUNGUN }, /* Windows XP SP2 and later (downloadable); Windows Vista and
later: Mapudungun (Chile) */
{ "mr", "IN", MARATHI }, /* Windows 2000 and later: Marathi (India) */
{ "moh", "CA", MOHAWK }, /* Windows XP SP2 and later (downloadable); Windows Vista and later:
Mohawk (Canada) */
{ "mn", "MN", MONGOLIAN }, /* Mongolian */
{ "ne", "NP", NEPALI }, /* Windows XP SP2 and later (downloadable); Windows Vista and later:
Nepali (Nepal) */
{ "nb", "NO", NORWEGIAN_BOKMAL }, /* Norwegian (Bokmal, Norway) */
{ "nn", "NO", NORWEGIAN_NYNORSK }, /* Norwegian (Nynorsk, Norway) */
{ "oc", "FR", OCCITAN }, /* Occitan (France) */
{ "or", "IN", ORIYA }, /* Oriya (India) */
{ "ps", "AF", PASHTO }, /* Windows XP SP2 and later (downloadable); Windows Vista and later:
Pashto (Afghanistan) */
{ "fa", "IR", FARSI }, /* Persian (Iran) */
{ "pl", "PL", POLISH }, /* Polish (Poland) */
{ "pt", "BR", PORTUGUESE_BRAZILIAN }, /* Portuguese (Brazil) */
{ "pt", "PT", PORTUGUESE_STANDARD }, /* Portuguese (Portugal) */
{ "pa", "IN", PUNJABI }, /* Windows XP and later: Punjabi (India) */
{ "quz", "BO", QUECHUA_BOLIVIA }, /* Windows XP SP2 and later: Quechua (Bolivia) */
{ "quz", "EC", QUECHUA_ECUADOR }, /* Windows XP SP2 and later: Quechua (Ecuador) */
{ "quz", "PE", QUECHUA_PERU }, /* Windows XP SP2 and later: Quechua (Peru) */
{ "ro", "RO", ROMANIAN }, /* Romanian (Romania) */
{ "rm", "CH", ROMANSH }, /* Windows XP SP2 and later (downloadable); Windows Vista and later:
Romansh (Switzerland) */
{ "ru", "RU", RUSSIAN }, /* Russian (Russia) */
{ "smn", "FI", SAMI_INARI }, /* Windows XP SP2 and later: Sami (Inari, Finland) */
{ "smj", "NO", SAMI_LULE_NORWAY }, /* Windows XP SP2 and later: Sami (Lule, Norway) */
{ "smj", "SE", SAMI_LULE_SWEDEN }, /* Windows XP SP2 and later: Sami (Lule, Sweden) */
{ "se", "FI", SAMI_NORTHERN_FINLAND }, /* Windows XP SP2 and later: Sami (Northern, Finland) */
{ "se", "NO", SAMI_NORTHERN_NORWAY }, /* Windows XP SP2 and later: Sami (Northern, Norway) */
{ "se", "SE", SAMI_NORTHERN_SWEDEN }, /* Windows XP SP2 and later: Sami (Northern, Sweden) */
{ "sms", "FI", SAMI_SKOLT }, /* Windows XP SP2 and later: Sami (Skolt, Finland) */
{ "sma", "NO", SAMI_SOUTHERN_NORWAY }, /* Windows XP SP2 and later: Sami (Southern, Norway) */
{ "sma", "SE", SAMI_SOUTHERN_SWEDEN }, /* Windows XP SP2 and later: Sami (Southern, Sweden) */
{ "sa", "IN", SANSKRIT }, /* Windows 2000 and later: Sanskrit (India) */
{ "sr", "SP", SERBIAN_LATIN }, /* Serbian (Latin) */
{ "sr", "SIH",
SERBIAN_LATIN_BOSNIA_HERZEGOVINA }, /* Serbian (Latin) (Bosnia and Herzegovina) */
{ "sr", "Cyrl_SP", SERBIAN_CYRILLIC }, /* Serbian (Cyrillic) */
{ "sr", "Cyrl_SIH",
SERBIAN_CYRILLIC_BOSNIA_HERZEGOVINA }, /* Serbian (Cyrillic) (Bosnia and Herzegovina) */
{ "ns", "ZA", SESOTHO_SA_LEBOA }, /* Windows XP SP2 and later: Sesotho sa Leboa/Northern Sotho
(South Africa) */
{ "tn", "ZA", TSWANA }, /* Windows XP SP2 and later: Setswana/Tswana (South Africa) */
{ "si", "LK", SINHALA }, /* Windows Vista and later: Sinhala (Sri Lanka) */
{ "sk", "SK", SLOVAK }, /* Slovak (Slovakia) */
{ "sl", "SI", SLOVENIAN }, /* Slovenian (Slovenia) */
{ "es", "AR", SPANISH_ARGENTINA }, /* Spanish (Argentina) */
{ "es", "BO", SPANISH_BOLIVIA }, /* Spanish (Bolivia) */
{ "es", "CL", SPANISH_CHILE }, /* Spanish (Chile) */
{ "es", "CO", SPANISH_COLOMBIA }, /* Spanish (Colombia) */
{ "es", "CR", SPANISH_COSTA_RICA }, /* Spanish (Costa Rica) */
{ "es", "DO", SPANISH_DOMINICAN_REPUBLIC }, /* Spanish (Dominican Republic) */
{ "es", "EC", SPANISH_ECUADOR }, /* Spanish (Ecuador) */
{ "es", "SV", SPANISH_EL_SALVADOR }, /* Spanish (El Salvador) */
{ "es", "GT", SPANISH_GUATEMALA }, /* Spanish (Guatemala) */
{ "es", "HN", SPANISH_HONDURAS }, /* Spanish (Honduras) */
{ "es", "MX", SPANISH_MEXICAN }, /* Spanish (Mexico) */
{ "es", "NI", SPANISH_NICARAGUA }, /* Spanish (Nicaragua) */
{ "es", "PA", SPANISH_PANAMA }, /* Spanish (Panama) */
{ "es", "PY", SPANISH_PARAGUAY }, /* Spanish (Paraguay) */
{ "es", "PE", SPANISH_PERU }, /* Spanish (Peru) */
{ "es", "PR", SPANISH_PUERTO_RICO }, /* Spanish (Puerto Rico) */
{ "es", "ES", SPANISH_MODERN_SORT }, /* Spanish (Spain) */
{ "es", "ES", SPANISH_TRADITIONAL_SORT }, /* Spanish (Spain, Traditional Sort) */
{ "es", "US", SPANISH_UNITED_STATES }, /* Windows Vista and later: Spanish (United States) */
{ "es", "UY", SPANISH_URUGUAY }, /* Spanish (Uruguay) */
{ "es", "VE", SPANISH_VENEZUELA }, /* Spanish (Venezuela) */
{ "sw", "KE", SWAHILI }, /* Windows 2000 and later: Swahili (Kenya) */
{ "sv", "FI", SWEDISH_FINLAND }, /* Swedish (Finland) */
{ "sv", "SE", SWEDISH }, /* Swedish (Sweden) */
{ "syr", "SY", SYRIAC }, /* Windows XP and later: Syriac (Syria) */
{ "ta", "IN", TAMIL }, /* Windows 2000 and later: Tamil (India) */
{ "tt", "RU", TATAR }, /* Windows XP and later: Tatar (Russia) */
{ "te", "IN", TELUGU }, /* Windows XP and later: Telugu (India) */
{ "th", "TH", THAI }, /* Thai (Thailand) */
{ "bo", "BT", TIBETAN_BHUTAN }, /* Windows Vista and later: Tibetan (Bhutan) */
{ "bo", "CN", TIBETAN_PRC }, /* Windows Vista and later: Tibetan (PRC) */
{ "tr", "TR", TURKISH }, /* Turkish (Turkey) */
{ "tk", "TM", TURKMEN }, /* Windows Vista and later: Turkmen (Turkmenistan) */
{ "ug", "CN", UIGHUR }, /* Windows Vista and later: Uighur (PRC) */
{ "uk", "UA", UKRAINIAN }, /* Ukrainian (Ukraine) */
{ "wen", "DE", UPPER_SORBIAN }, /* Windows Vista and later: Upper Sorbian (Germany) */
{ "tr", "IN", URDU_INDIA }, /* Urdu (India) */
{ "ur", "PK", URDU }, /* Windows 98/Me, Windows 2000 and later: Urdu (Pakistan) */
{ "uz", "UZ", UZBEK_LATIN }, /* Uzbek (Latin) */
{ "uz", "Cyrl_UZ", UZBEK_CYRILLIC }, /* Uzbek (Cyrillic) */
{ "vi", "VN", VIETNAMESE }, /* Windows 98/Me, Windows NT 4.0 and later: Vietnamese (Vietnam) */
{ "cy", "GB", WELSH }, /* Windows XP SP2 and later: Welsh (United Kingdom) */
{ "wo", "SN", WOLOF }, /* Windows Vista and later: Wolof (Senegal) */
{ "xh", "ZA", XHOSA }, /* Windows XP SP2 and later: Xhosa/isiXhosa (South Africa) */
{ "sah", "RU", YAKUT }, /* Windows Vista and later: Yakut (Russia) */
{ "ii", "CN", YI }, /* Windows Vista and later: Yi (PRC) */
{ "yo", "NG", YORUBA }, /* Windows Vista and later: Yoruba (Nigeria) */
{ "zu", "ZA", ZULU } /* Windows XP SP2 and later: Zulu/isiZulu (South Africa) */
};
typedef struct
{
DWORD localeId;
const char* name;
} LOCALE_NAME;
static const LOCALE_NAME LOCALE_NAME_TABLE[] = {
{ AFRIKAANS, "AFRIKAANS" },
{ ALBANIAN, "ALBANIAN" },
{ ALSATIAN, "ALSATIAN" },
{ AMHARIC, "AMHARIC" },
{ ARABIC_SAUDI_ARABIA, "ARABIC_SAUDI_ARABIA" },
{ ARABIC_IRAQ, "ARABIC_IRAQ" },
{ ARABIC_EGYPT, "ARABIC_EGYPT" },
{ ARABIC_LIBYA, "ARABIC_LIBYA" },
{ ARABIC_ALGERIA, "ARABIC_ALGERIA" },
{ ARABIC_MOROCCO, "ARABIC_MOROCCO" },
{ ARABIC_TUNISIA, "ARABIC_TUNISIA" },
{ ARABIC_OMAN, "ARABIC_OMAN" },
{ ARABIC_YEMEN, "ARABIC_YEMEN" },
{ ARABIC_SYRIA, "ARABIC_SYRIA" },
{ ARABIC_JORDAN, "ARABIC_JORDAN" },
{ ARABIC_LEBANON, "ARABIC_LEBANON" },
{ ARABIC_KUWAIT, "ARABIC_KUWAIT" },
{ ARABIC_UAE, "ARABIC_UAE" },
{ ARABIC_BAHRAIN, "ARABIC_BAHRAIN" },
{ ARABIC_QATAR, "ARABIC_QATAR" },
{ ARMENIAN, "ARMENIAN" },
{ ASSAMESE, "ASSAMESE" },
{ AZERI_LATIN, "AZERI_LATIN" },
{ AZERI_CYRILLIC, "AZERI_CYRILLIC" },
{ BASHKIR, "BASHKIR" },
{ BASQUE, "BASQUE" },
{ BELARUSIAN, "BELARUSIAN" },
{ BENGALI_INDIA, "BENGALI_INDIA" },
{ BOSNIAN_LATIN, "BOSNIAN_LATIN" },
{ BRETON, "BRETON" },
{ BULGARIAN, "BULGARIAN" },
{ CATALAN, "CATALAN" },
{ CHINESE_TAIWAN, "CHINESE_TAIWAN" },
{ CHINESE_PRC, "CHINESE_PRC" },
{ CHINESE_HONG_KONG, "CHINESE_HONG_KONG" },
{ CHINESE_SINGAPORE, "CHINESE_SINGAPORE" },
{ CHINESE_MACAU, "CHINESE_MACAU" },
{ CROATIAN, "CROATIAN" },
{ CROATIAN_BOSNIA_HERZEGOVINA, "CROATIAN_BOSNIA_HERZEGOVINA" },
{ CZECH, "CZECH" },
{ DANISH, "DANISH" },
{ DARI, "DARI" },
{ DIVEHI, "DIVEHI" },
{ DUTCH_STANDARD, "DUTCH_STANDARD" },
{ DUTCH_BELGIAN, "DUTCH_BELGIAN" },
{ ENGLISH_UNITED_STATES, "ENGLISH_UNITED_STATES" },
{ ENGLISH_UNITED_KINGDOM, "ENGLISH_UNITED_KINGDOM" },
{ ENGLISH_AUSTRALIAN, "ENGLISH_AUSTRALIAN" },
{ ENGLISH_CANADIAN, "ENGLISH_CANADIAN" },
{ ENGLISH_NEW_ZEALAND, "ENGLISH_NEW_ZEALAND" },
{ ENGLISH_INDIA, "ENGLISH_INDIA" },
{ ENGLISH_IRELAND, "ENGLISH_IRELAND" },
{ ENGLISH_MALAYSIA, "ENGLISH_MALAYSIA" },
{ ENGLISH_SOUTH_AFRICA, "ENGLISH_SOUTH_AFRICA" },
{ ENGLISH_JAMAICA, "ENGLISH_JAMAICA" },
{ ENGLISH_CARIBBEAN, "ENGLISH_CARIBBEAN" },
{ ENGLISH_BELIZE, "ENGLISH_BELIZE" },
{ ENGLISH_TRINIDAD, "ENGLISH_TRINIDAD" },
{ ENGLISH_ZIMBABWE, "ENGLISH_ZIMBABWE" },
{ ENGLISH_PHILIPPINES, "ENGLISH_PHILIPPINES" },
{ ENGLISH_SINGAPORE, "ENGLISH_SINGAPORE" },
{ ESTONIAN, "ESTONIAN" },
{ FAEROESE, "FAEROESE" },
{ FARSI, "FARSI" },
{ FILIPINO, "FILIPINO" },
{ FINNISH, "FINNISH" },
{ FRENCH_STANDARD, "FRENCH_STANDARD" },
{ FRENCH_BELGIAN, "FRENCH_BELGIAN" },
{ FRENCH_CANADIAN, "FRENCH_CANADIAN" },
{ FRENCH_SWISS, "FRENCH_SWISS" },
{ FRENCH_LUXEMBOURG, "FRENCH_LUXEMBOURG" },
{ FRENCH_MONACO, "FRENCH_MONACO" },
{ FRISIAN, "FRISIAN" },
{ GEORGIAN, "GEORGIAN" },
{ GALICIAN, "GALICIAN" },
{ GERMAN_STANDARD, "GERMAN_STANDARD" },
{ GERMAN_SWISS, "GERMAN_SWISS" },
{ GERMAN_AUSTRIAN, "GERMAN_AUSTRIAN" },
{ GERMAN_LUXEMBOURG, "GERMAN_LUXEMBOURG" },
{ GERMAN_LIECHTENSTEIN, "GERMAN_LIECHTENSTEIN" },
{ GREEK, "GREEK" },
{ GREENLANDIC, "GREENLANDIC" },
{ GUJARATI, "GUJARATI" },
{ HEBREW, "HEBREW" },
{ HINDI, "HINDI" },
{ HUNGARIAN, "HUNGARIAN" },
{ ICELANDIC, "ICELANDIC" },
{ IGBO, "IGBO" },
{ INDONESIAN, "INDONESIAN" },
{ IRISH, "IRISH" },
{ ITALIAN_STANDARD, "ITALIAN_STANDARD" },
{ ITALIAN_SWISS, "ITALIAN_SWISS" },
{ JAPANESE, "JAPANESE" },
{ KANNADA, "KANNADA" },
{ KAZAKH, "KAZAKH" },
{ KHMER, "KHMER" },
{ KICHE, "KICHE" },
{ KINYARWANDA, "KINYARWANDA" },
{ KONKANI, "KONKANI" },
{ KOREAN, "KOREAN" },
{ KYRGYZ, "KYRGYZ" },
{ LAO, "LAO" },
{ LATVIAN, "LATVIAN" },
{ LITHUANIAN, "LITHUANIAN" },
{ LOWER_SORBIAN, "LOWER_SORBIAN" },
{ LUXEMBOURGISH, "LUXEMBOURGISH" },
{ MACEDONIAN, "MACEDONIAN" },
{ MALAY_MALAYSIA, "MALAY_MALAYSIA" },
{ MALAY_BRUNEI_DARUSSALAM, "MALAY_BRUNEI_DARUSSALAM" },
{ MALAYALAM, "MALAYALAM" },
{ MALTESE, "MALTESE" },
{ MAPUDUNGUN, "MAPUDUNGUN" },
{ MAORI, "MAORI" },
{ MARATHI, "MARATHI" },
{ MOHAWK, "MOHAWK" },
{ MONGOLIAN, "MONGOLIAN" },
{ NEPALI, "NEPALI" },
{ NORWEGIAN_BOKMAL, "NORWEGIAN_BOKMAL" },
{ NORWEGIAN_NYNORSK, "NORWEGIAN_NYNORSK" },
{ OCCITAN, "OCCITAN" },
{ ORIYA, "ORIYA" },
{ PASHTO, "PASHTO" },
{ POLISH, "POLISH" },
{ PORTUGUESE_BRAZILIAN, "PORTUGUESE_BRAZILIAN" },
{ PORTUGUESE_STANDARD, "PORTUGUESE_STANDARD" },
{ PUNJABI, "PUNJABI" },
{ QUECHUA_BOLIVIA, "QUECHUA_BOLIVIA" },
{ QUECHUA_ECUADOR, "QUECHUA_ECUADOR" },
{ QUECHUA_PERU, "QUECHUA_PERU" },
{ ROMANIAN, "ROMANIAN" },
{ ROMANSH, "ROMANSH" },
{ RUSSIAN, "RUSSIAN" },
{ SAMI_INARI, "SAMI_INARI" },
{ SAMI_LULE_NORWAY, "SAMI_LULE_NORWAY" },
{ SAMI_LULE_SWEDEN, "SAMI_LULE_SWEDEN" },
{ SAMI_NORTHERN_FINLAND, "SAMI_NORTHERN_FINLAND" },
{ SAMI_NORTHERN_NORWAY, "SAMI_NORTHERN_NORWAY" },
{ SAMI_NORTHERN_SWEDEN, "SAMI_NORTHERN_SWEDEN" },
{ SAMI_SKOLT, "SAMI_SKOLT" },
{ SAMI_SOUTHERN_NORWAY, "SAMI_SOUTHERN_NORWAY" },
{ SAMI_SOUTHERN_SWEDEN, "SAMI_SOUTHERN_SWEDEN" },
{ SANSKRIT, "SANSKRIT" },
{ SERBIAN_LATIN, "SERBIAN_LATIN" },
{ SERBIAN_LATIN_BOSNIA_HERZEGOVINA, "SERBIAN_LATIN_BOSNIA_HERZEGOVINA" },
{ SERBIAN_CYRILLIC, "SERBIAN_CYRILLIC" },
{ SERBIAN_CYRILLIC_BOSNIA_HERZEGOVINA, "SERBIAN_CYRILLIC_BOSNIA_HERZEGOVINA" },
{ SESOTHO_SA_LEBOA, "SESOTHO_SA_LEBOA" },
{ SINHALA, "SINHALA" },
{ SLOVAK, "SLOVAK" },
{ SLOVENIAN, "SLOVENIAN" },
{ SPANISH_TRADITIONAL_SORT, "SPANISH_TRADITIONAL_SORT" },
{ SPANISH_MEXICAN, "SPANISH_MEXICAN" },
{ SPANISH_MODERN_SORT, "SPANISH_MODERN_SORT" },
{ SPANISH_GUATEMALA, "SPANISH_GUATEMALA" },
{ SPANISH_COSTA_RICA, "SPANISH_COSTA_RICA" },
{ SPANISH_PANAMA, "SPANISH_PANAMA" },
{ SPANISH_DOMINICAN_REPUBLIC, "SPANISH_DOMINICAN_REPUBLIC" },
{ SPANISH_VENEZUELA, "SPANISH_VENEZUELA" },
{ SPANISH_COLOMBIA, "SPANISH_COLOMBIA" },
{ SPANISH_PERU, "SPANISH_PERU" },
{ SPANISH_ARGENTINA, "SPANISH_ARGENTINA" },
{ SPANISH_ECUADOR, "SPANISH_ECUADOR" },
{ SPANISH_CHILE, "SPANISH_CHILE" },
{ SPANISH_UNITED_STATES, "SPANISH_UNITED_STATES" },
{ SPANISH_URUGUAY, "SPANISH_URUGUAY" },
{ SPANISH_PARAGUAY, "SPANISH_PARAGUAY" },
{ SPANISH_BOLIVIA, "SPANISH_BOLIVIA" },
{ SPANISH_EL_SALVADOR, "SPANISH_EL_SALVADOR" },
{ SPANISH_HONDURAS, "SPANISH_HONDURAS" },
{ SPANISH_NICARAGUA, "SPANISH_NICARAGUA" },
{ SPANISH_PUERTO_RICO, "SPANISH_PUERTO_RICO" },
{ SWAHILI, "SWAHILI" },
{ SWEDISH, "SWEDISH" },
{ SWEDISH_FINLAND, "SWEDISH_FINLAND" },
{ SYRIAC, "SYRIAC" },
{ TAMIL, "TAMIL" },
{ TATAR, "TATAR" },
{ TELUGU, "TELUGU" },
{ THAI, "THAI" },
{ TIBETAN_BHUTAN, "TIBETAN_BHUTAN" },
{ TIBETAN_PRC, "TIBETAN_PRC" },
{ TSWANA, "TSWANA" },
{ UKRAINIAN, "UKRAINIAN" },
{ TURKISH, "TURKISH" },
{ TURKMEN, "TURKMEN" },
{ UIGHUR, "UIGHUR" },
{ UPPER_SORBIAN, "UPPER_SORBIAN" },
{ URDU, "URDU" },
{ URDU_INDIA, "URDU_INDIA" },
{ UZBEK_LATIN, "UZBEK_LATIN" },
{ UZBEK_CYRILLIC, "UZBEK_CYRILLIC" },
{ VIETNAMESE, "VIETNAMESE" },
{ WELSH, "WELSH" },
{ WOLOF, "WOLOF" },
{ XHOSA, "XHOSA" },
{ YAKUT, "YAKUT" },
{ YI, "YI" },
{ YORUBA, "YORUBA" },
{ ZULU, "ZULU" }
};
typedef struct
{
DWORD locale; /* Locale ID */
DWORD keyboardLayouts[5]; /* array of associated keyboard layouts */
} LOCALE_KEYBOARD_LAYOUTS;
/* TODO: Use KBD_* defines instead of hardcoded values */
static const LOCALE_KEYBOARD_LAYOUTS LOCALE_KEYBOARD_LAYOUTS_TABLE[] = {
{ AFRIKAANS, { 0x00000409, 0x00000409, 0x0, 0x0, 0x0 } },
{ ALBANIAN, { 0x0000041c, 0x00000409, 0x0, 0x0, 0x0 } },
{ ARABIC_SAUDI_ARABIA, { 0x00000409, 0x00000401, 0x0, 0x0, 0x0 } },
{ ARABIC_IRAQ, { 0x00000409, 0x00000401, 0x0, 0x0, 0x0 } },
{ ARABIC_EGYPT, { 0x00000409, 0x00000401, 0x0, 0x0, 0x0 } },
{ ARABIC_LIBYA, { 0x0000040c, 0x00020401, 0x0, 0x0, 0x0 } },
{ ARABIC_ALGERIA, { 0x0000040c, 0x00020401, 0x0, 0x0, 0x0 } },
{ ARABIC_MOROCCO, { 0x0000040c, 0x00020401, 0x0, 0x0, 0x0 } },
{ ARABIC_TUNISIA, { 0x0000040c, 0x00020401, 0x0, 0x0, 0x0 } },
{ ARABIC_OMAN, { 0x00000409, 0x00000401, 0x0, 0x0, 0x0 } },
{ ARABIC_YEMEN, { 0x00000409, 0x00000401, 0x0, 0x0, 0x0 } },
{ ARABIC_SYRIA, { 0x00000409, 0x00000401, 0x0, 0x0, 0x0 } },
{ ARABIC_JORDAN, { 0x00000409, 0x00000401, 0x0, 0x0, 0x0 } },
{ ARABIC_LEBANON, { 0x00000409, 0x00000401, 0x0, 0x0, 0x0 } },
{ ARABIC_KUWAIT, { 0x00000409, 0x00000401, 0x0, 0x0, 0x0 } },
{ ARABIC_UAE, { 0x00000409, 0x00000401, 0x0, 0x0, 0x0 } },
{ ARABIC_BAHRAIN, { 0x00000409, 0x00000401, 0x0, 0x0, 0x0 } },
{ ARABIC_QATAR, { 0x00000409, 0x00000401, 0x0, 0x0, 0x0 } },
{ ARMENIAN, { 0x0000042b, 0x00000409, 0x00000419, 0x0, 0x0 } },
{ AZERI_LATIN, { 0x0000042c, 0x0000082c, 0x00000419, 0x0, 0x0 } },
{ AZERI_CYRILLIC, { 0x0000082c, 0x0000042c, 0x00000419, 0x0, 0x0 } },
{ BASQUE, { 0x0000040a, 0x00000409, 0x0, 0x0, 0x0 } },
{ BELARUSIAN, { 0x00000423, 0x00000409, 0x00000419, 0x0, 0x0 } },
{ BENGALI_INDIA, { 0x00000445, 0x00000409, 0x0, 0x0, 0x0 } },
{ BOSNIAN_LATIN, { 0x0000141A, 0x00000409, 0x0, 0x0, 0x0 } },
{ BULGARIAN, { 0x00000402, 0x00000409, 0x0, 0x0, 0x0 } },
{ CATALAN, { 0x0000040a, 0x00000409, 0x0, 0x0, 0x0 } },
{ CHINESE_TAIWAN, { 0x00000404, 0xe0080404, 0xE0010404, 0x0, 0x0 } },
{ CHINESE_PRC, { 0x00000804, 0xe00e0804, 0xe0010804, 0xe0030804, 0xe0040804 } },
{ CHINESE_HONG_KONG, { 0x00000409, 0xe0080404, 0x0, 0x0, 0x0 } },
{ CHINESE_SINGAPORE, { 0x00000409, 0xe00e0804, 0xe0010804, 0xe0030804, 0xe0040804 } },
{ CHINESE_MACAU, { 0x00000409, 0xe00e0804, 0xe0020404, 0xe0080404 } },
{ CROATIAN, { 0x0000041a, 0x00000409, 0x0, 0x0, 0x0 } },
{ CROATIAN_BOSNIA_HERZEGOVINA, { 0x0000041a, 0x00000409, 0x0, 0x0, 0x0 } },
{ CZECH, { 0x00000405, 0x00000409, 0x0, 0x0, 0x0 } },
{ DANISH, { 0x00000406, 0x00000409, 0x0, 0x0, 0x0 } },
{ DIVEHI, { 0x00000409, 0x00000465, 0x0, 0x0, 0x0 } },
{ DUTCH_STANDARD, { 0x00020409, 0x00000413, 0x00000409, 0x0, 0x0 } },
{ DUTCH_BELGIAN, { 0x00000813, 0x00000409, 0x0, 0x0, 0x0 } },
{ ENGLISH_UNITED_STATES, { 0x00000409, 0x0, 0x0, 0x0, 0x0 } },
{ ENGLISH_UNITED_KINGDOM, { 0x00000809, 0x0, 0x0, 0x0, 0x0 } },
{ ENGLISH_AUSTRALIAN, { 0x00000409, 0x0, 0x0, 0x0, 0x0 } },
{ ENGLISH_CANADIAN, { 0x00000409, 0x00011009, 0x00001009, 0x0, 0x0 } },
{ ENGLISH_NEW_ZEALAND, { 0x00000409, 0x0, 0x0, 0x0, 0x0 } },
{ ENGLISH_IRELAND, { 0x00001809, 0x00011809, 0x0, 0x0, 0x0 } },
{ ENGLISH_SOUTH_AFRICA, { 0x00000409, 0x0, 0x0, 0x0, 0x0 } },
{ ENGLISH_JAMAICA, { 0x00000409, 0x0, 0x0, 0x0, 0x0 } },
{ ENGLISH_CARIBBEAN, { 0x00000409, 0x0, 0x0, 0x0, 0x0 } },
{ ENGLISH_BELIZE, { 0x00000409, 0x0, 0x0, 0x0, 0x0 } },
{ ENGLISH_TRINIDAD, { 0x00000409, 0x0, 0x0, 0x0, 0x0 } },
{ ENGLISH_ZIMBABWE, { 0x00000409, 0x0, 0x0, 0x0, 0x0 } },
{ ENGLISH_PHILIPPINES, { 0x00000409, 0x0, 0x0, 0x0, 0x0 } },
{ ESTONIAN, { 0x00000425, 0x0, 0x0, 0x0, 0x0 } },
{ FAEROESE, { 0x00000406, 0x00000409, 0x0, 0x0, 0x0 } },
{ FARSI, { 0x00000409, 0x00000429, 0x00000401, 0x0, 0x0 } },
{ FINNISH, { 0x0000040b, 0x00000409, 0x0, 0x0, 0x0 } },
{ FRENCH_STANDARD, { 0x0000040c, 0x00000409, 0x0, 0x0, 0x0 } },
{ FRENCH_BELGIAN, { 0x0000080c, 0x00000409, 0x0, 0x0, 0x0 } },
{ FRENCH_CANADIAN, { 0x00000C0C, 0x00011009, 0x00000409, 0x0, 0x0 } },
{ FRENCH_SWISS, { 0x0000100c, 0x00000409, 0x0, 0x0, 0x0 } },
{ FRENCH_LUXEMBOURG, { 0x0000040c, 0x00000409, 0x0, 0x0, 0x0 } },
{ FRENCH_MONACO, { 0x0000040c, 0x00000409, 0x0, 0x0, 0x0 } },
{ GEORGIAN, { 0x00000437, 0x00000409, 0x00000419, 0x0, 0x0 } },
{ GALICIAN, { 0x0000040a, 0x00000409, 0x0, 0x0, 0x0 } },
{ GERMAN_STANDARD, { 0x00000407, 0x00000409, 0x0, 0x0, 0x0 } },
{ GERMAN_SWISS, { 0x00000807, 0x00000409, 0x0, 0x0, 0x0 } },
{ GERMAN_AUSTRIAN, { 0x00000407, 0x00000409, 0x0, 0x0, 0x0 } },
{ GERMAN_LUXEMBOURG, { 0x00000407, 0x00000409, 0x0, 0x0, 0x0 } },
{ GERMAN_LIECHTENSTEIN, { 0x00000407, 0x00000409, 0x0, 0x0, 0x0 } },
{ GREEK, { 0x00000408, 0x00000409, 0x0, 0x0, 0x0 } },
{ GUJARATI, { 0x00000409, 0x00000447, 0x00010439, 0x0, 0x0 } },
{ HEBREW, { 0x00000409, 0x0000040d, 0x0, 0x0, 0x0 } },
{ HINDI, { 0x00000409, 0x00010439, 0x00000439, 0x0, 0x0 } },
{ HUNGARIAN, { 0x0000040e, 0x00000409, 0x0, 0x0, 0x0 } },
{ ICELANDIC, { 0x0000040f, 0x00000409, 0x0, 0x0, 0x0 } },
{ INDONESIAN, { 0x00000409, 0x00000409, 0x0, 0x0, 0x0 } },
{ ITALIAN_STANDARD, { 0x00000410, 0x00000409, 0x0, 0x0, 0x0 } },
{ ITALIAN_SWISS, { 0x00000410, 0x00000409, 0x0, 0x0, 0x0 } },
{ JAPANESE, { 0xe0010411, 0x0, 0x0, 0x0, 0x0 } },
{ KANNADA, { 0x00000409, 0x0000044b, 0x00010439, 0x0, 0x0 } },
{ KAZAKH, { 0x0000043f, 0x00000409, 0x00000419, 0x0, 0x0 } },
{ KONKANI, { 0x00000409, 0x00000439, 0x0, 0x0, 0x0 } },
{ KOREAN, { 0xE0010412, 0x0, 0x0, 0x0, 0x0 } },
{ KYRGYZ, { 0x00000440, 0x00000409, 0x0, 0x0, 0x0 } },
{ LATVIAN, { 0x00010426, 0x0, 0x0, 0x0, 0x0 } },
{ LITHUANIAN, { 0x00010427, 0x0, 0x0, 0x0, 0x0 } },
{ MACEDONIAN, { 0x0000042f, 0x00000409, 0x0, 0x0, 0x0 } },
{ MALAY_MALAYSIA, { 0x00000409, 0x0, 0x0, 0x0, 0x0 } },
{ MALAY_BRUNEI_DARUSSALAM, { 0x00000409, 0x0, 0x0, 0x0, 0x0 } },
{ MALAYALAM, { 0x00000409, 0x0000044c, 0x0, 0x0, 0x0 } },
{ MALTESE, { 0x00000409, 0x0000043a, 0x0, 0x0, 0x0 } },
{ MAORI, { 0x00000409, 0x00000481, 0x0, 0x0, 0x0 } },
{ MARATHI, { 0x00000409, 0x0000044e, 0x00000439, 0x0, 0x0 } },
{ MONGOLIAN, { 0x00000450, 0x00000409, 0x0, 0x0, 0x0 } },
{ NORWEGIAN_BOKMAL, { 0x00000414, 0x00000409, 0x0, 0x0, 0x0 } },
{ NORWEGIAN_NYNORSK, { 0x00000414, 0x00000409, 0x0, 0x0, 0x0 } },
{ POLISH, { 0x00010415, 0x00000415, 0x00000409, 0x0, 0x0 } },
{ PORTUGUESE_BRAZILIAN, { 0x00000416, 0x00000409, 0x0, 0x0, 0x0 } },
{ PORTUGUESE_STANDARD, { 0x00000816, 0x00000409, 0x0, 0x0, 0x0 } },
{ PUNJABI, { 0x00000409, 0x00000446, 0x00010439, 0x0, 0x0 } },
{ QUECHUA_BOLIVIA, { 0x00000409, 0x0000080A, 0x0, 0x0, 0x0 } },
{ QUECHUA_ECUADOR, { 0x00000409, 0x0000080A, 0x0, 0x0, 0x0 } },
{ QUECHUA_PERU, { 0x00000409, 0x0000080A, 0x0, 0x0, 0x0 } },
{ ROMANIAN, { 0x00000418, 0x00000409, 0x0, 0x0, 0x0 } },
{ RUSSIAN, { 0x00000419, 0x00000409, 0x0, 0x0, 0x0 } },
{ SAMI_INARI, { 0x0001083b, 0x00000409, 0x0, 0x0, 0x0 } },
{ SAMI_LULE_NORWAY, { 0x0000043b, 0x00000409, 0x0, 0x0, 0x0 } },
{ SAMI_LULE_SWEDEN, { 0x0000083b, 0x00000409, 0x0, 0x0, 0x0 } },
{ SAMI_NORTHERN_FINLAND, { 0x0001083b, 0x00000409, 0x0, 0x0, 0x0 } },
{ SAMI_NORTHERN_NORWAY, { 0x0000043b, 0x00000409, 0x0, 0x0, 0x0 } },
{ SAMI_NORTHERN_SWEDEN, { 0x0000083b, 0x00000409, 0x0, 0x0, 0x0 } },
{ SAMI_SKOLT, { 0x0001083b, 0x00000409, 0x0, 0x0, 0x0 } },
{ SAMI_SOUTHERN_NORWAY, { 0x0000043b, 0x00000409, 0x0, 0x0, 0x0 } },
{ SAMI_SOUTHERN_SWEDEN, { 0x0000083b, 0x00000409, 0x0, 0x0, 0x0 } },
{ SANSKRIT, { 0x00000409, 0x00000439, 0x0, 0x0, 0x0 } },
{ SERBIAN_LATIN, { 0x0000081a, 0x00000409, 0x0, 0x0, 0x0 } },
{ SERBIAN_LATIN_BOSNIA_HERZEGOVINA, { 0x0000081a, 0x00000409, 0x0, 0x0, 0x0 } },
{ SERBIAN_CYRILLIC, { 0x00000c1a, 0x00000409, 0x0, 0x0, 0x0 } },
{ SERBIAN_CYRILLIC_BOSNIA_HERZEGOVINA, { 0x00000c1a, 0x00000409, 0x0, 0x0, 0x0 } },
{ SLOVAK, { 0x0000041b, 0x00000409, 0x0, 0x0, 0x0 } },
{ SLOVENIAN, { 0x00000424, 0x00000409, 0x0, 0x0, 0x0 } },
{ SPANISH_TRADITIONAL_SORT, { 0x0000040a, 0x00000409, 0x0, 0x0, 0x0 } },
{ SPANISH_MEXICAN, { 0x0000080a, 0x00000409, 0x0, 0x0, 0x0 } },
{ SPANISH_MODERN_SORT, { 0x0000040a, 0x00000409, 0x0, 0x0, 0x0 } },
{ SPANISH_GUATEMALA, { 0x0000080a, 0x00000409, 0x0, 0x0, 0x0 } },
{ SPANISH_COSTA_RICA, { 0x0000080a, 0x00000409, 0x0, 0x0, 0x0 } },
{ SPANISH_PANAMA, { 0x0000080a, 0x00000409, 0x0, 0x0, 0x0 } },
{ SPANISH_DOMINICAN_REPUBLIC, { 0x0000080a, 0x00000409, 0x0, 0x0, 0x0 } },
{ SPANISH_VENEZUELA, { 0x0000080a, 0x00000409, 0x0, 0x0, 0x0 } },
{ SPANISH_COLOMBIA, { 0x0000080a, 0x00000409, 0x0, 0x0, 0x0 } },
{ SPANISH_PERU, { 0x0000080a, 0x00000409, 0x0, 0x0, 0x0 } },
{ SPANISH_ARGENTINA, { 0x0000080a, 0x00000409, 0x0, 0x0, 0x0 } },
{ SPANISH_ECUADOR, { 0x0000080a, 0x00000409, 0x0, 0x0, 0x0 } },
{ SPANISH_CHILE, { 0x0000080a, 0x00000409, 0x0, 0x0, 0x0 } },
{ SPANISH_URUGUAY, { 0x0000080a, 0x00000409, 0x0, 0x0, 0x0 } },
{ SPANISH_PARAGUAY, { 0x0000080a, 0x00000409, 0x0, 0x0, 0x0 } },
{ SPANISH_BOLIVIA, { 0x0000080a, 0x00000409, 0x0, 0x0, 0x0 } },
{ SPANISH_EL_SALVADOR, { 0x0000080a, 0x00000409, 0x0, 0x0, 0x0 } },
{ SPANISH_HONDURAS, { 0x0000080a, 0x00000409, 0x0, 0x0, 0x0 } },
{ SPANISH_NICARAGUA, { 0x0000080a, 0x00000409, 0x0, 0x0, 0x0 } },
{ SPANISH_PUERTO_RICO, { 0x0000080a, 0x00000409, 0x0, 0x0, 0x0 } },
{ SWAHILI, { 0x00000409, 0x0, 0x0, 0x0, 0x0 } },
{ SWEDISH, { 0x0000041d, 0x00000409, 0x0, 0x0, 0x0 } },
{ SWEDISH_FINLAND, { 0x0000041d, 0x00000409, 0x0, 0x0, 0x0 } },
{ SYRIAC, { 0x00000409, 0x0000045a, 0x0, 0x0, 0x0 } },
{ TAMIL, { 0x00000409, 0x00000449, 0x0, 0x0, 0x0 } },
{ TATAR, { 0x00000444, 0x00000409, 0x00000419, 0x0, 0x0 } },
{ TELUGU, { 0x00000409, 0x0000044a, 0x00010439, 0x0, 0x0 } },
{ THAI, { 0x00000409, 0x0000041e, 0x0, 0x0, 0x0 } },
{ TSWANA, { 0x00000409, 0x0000041f, 0x0, 0x0, 0x0 } },
{ UKRAINIAN, { 0x00000422, 0x00000409, 0x0, 0x0, 0x0 } },
{ TURKISH, { 0x0000041f, 0x0000041f, 0x0, 0x0, 0x0 } },
{ UKRAINIAN, { 0x00000422, 0x00000409, 0x0, 0x0, 0x0 } },
{ URDU, { 0x00000401, 0x00000409, 0x0, 0x0, 0x0 } },
{ UZBEK_LATIN, { 0x00000409, 0x00000843, 0x00000419, 0x0, 0x0 } },
{ UZBEK_CYRILLIC, { 0x00000843, 0x00000409, 0x00000419, 0x0, 0x0 } },
{ VIETNAMESE, { 0x00000409, 0x0000042a, 0x0, 0x0, 0x0 } },
{ WELSH, { 0x00000452, 0x00000809, 0x0, 0x0, 0x0 } },
{ XHOSA, { 0x00000409, 0x00000409, 0x0, 0x0, 0x0 } },
};
static BOOL freerdp_get_system_language_and_country_codes(char* language, size_t languageLen,
char* country, size_t countryLen)
{
WINPR_ASSERT(language);
WINPR_ASSERT(languageLen > 0);
WINPR_ASSERT(country);
WINPR_ASSERT(countryLen);
#if defined(__APPLE__)
{
CFStringRef langRef, countryRef;
CFLocaleRef localeRef = CFLocaleCopyCurrent();
if (!localeRef)
return FALSE;
langRef = (CFStringRef)CFLocaleGetValue(localeRef, kCFLocaleLanguageCode);
countryRef = (CFStringRef)CFLocaleGetValue(localeRef, kCFLocaleCountryCode);
if (!langRef || !countryRef)
{
CFRelease(localeRef);
return FALSE;
}
if (!CFStringGetCString(langRef, language, languageLen, kCFStringEncodingUTF8) ||
!CFStringGetCString(countryRef, country, countryLen, kCFStringEncodingUTF8))
{
CFRelease(localeRef);
return FALSE;
}
CFRelease(localeRef);
return TRUE;
}
#else
{
size_t dot = 0;
DWORD nSize = 0;
size_t underscore = 0;
char* env_lang = nullptr;
LPCSTR lang = "LANG";
/* LANG = <language>_<country>.<encoding> */
nSize = GetEnvironmentVariableA(lang, nullptr, 0);
if (!nSize)
return FALSE; /* LANG environment variable was not set */
env_lang = (char*)malloc(nSize);
if (!env_lang)
return FALSE;
if (GetEnvironmentVariableA(lang, env_lang, nSize) !=
nSize - 1) /* Get locale from environment variable LANG */
{
free(env_lang);
return FALSE;
}
underscore = strcspn(env_lang, "_");
if (underscore > 3)
{
free(env_lang);
return FALSE; /* The language name should not be more than 3 letters long */
}
else
{
/* Get language code */
size_t len = MIN(languageLen - 1u, underscore);
strncpy(language, env_lang, len);
language[len] = '\0';
}
dot = strcspn(env_lang, ".");
/* Get country code */
if (dot > underscore)
{
size_t len = MIN(countryLen - 1, dot - underscore - 1);
strncpy(country, &env_lang[underscore + 1], len);
country[len] = '\0';
}
else
{
free(env_lang);
return FALSE; /* Invalid locale */
}
free(env_lang);
return TRUE;
}
#endif
}
static const SYSTEM_LOCALE* freerdp_detect_system_locale(void)
{
char language[LOCALE_LANGUAGE_LEN] = WINPR_C_ARRAY_INIT;
char country[LOCALE_COUNTRY_LEN] = WINPR_C_ARRAY_INIT;
const SYSTEM_LOCALE* locale = nullptr;
freerdp_get_system_language_and_country_codes(language, ARRAYSIZE(language), country,
ARRAYSIZE(country));
for (size_t i = 0; i < ARRAYSIZE(SYSTEM_LOCALE_TABLE); i++)
{
const SYSTEM_LOCALE* current = &SYSTEM_LOCALE_TABLE[i];
if ((strcmp(language, current->language) == 0) && (strcmp(country, current->country) == 0))
{
locale = current;
break;
}
}
return locale;
}
DWORD freerdp_get_system_locale_id(void)
{
const SYSTEM_LOCALE* locale = nullptr;
locale = freerdp_detect_system_locale();
if (locale != nullptr)
return locale->code;
return 0;
}
static const SYSTEM_LOCALE* get_locale_from_str(const char* name)
{
for (size_t i = 0; i < ARRAYSIZE(SYSTEM_LOCALE_TABLE); i++)
{
char buffer[LOCALE_LANGUAGE_LEN + LOCALE_COUNTRY_LEN + 2] = WINPR_C_ARRAY_INIT;
const SYSTEM_LOCALE* current = &SYSTEM_LOCALE_TABLE[i];
(void)_snprintf(buffer, sizeof(buffer), "%s_%s", current->language, current->country);
/* full match, e.g. en_US */
if ((strcmp(name, buffer) == 0))
return current;
/* simple language only match e.g. 'en' */
else if ((strcmp(name, current->language) == 0))
return current;
}
return nullptr;
}
static INT64 get_layout_from_locale(const SYSTEM_LOCALE* locale)
{
for (size_t i = 0; i < ARRAYSIZE(LOCALE_KEYBOARD_LAYOUTS_TABLE); i++)
{
const LOCALE_KEYBOARD_LAYOUTS* current = &LOCALE_KEYBOARD_LAYOUTS_TABLE[i];
WINPR_ASSERT(current);
if (current->locale == locale->code)
{
/* Locale found in list of default keyboard layouts */
for (size_t j = 0; j < 5; j++)
{
if (current->keyboardLayouts[j] == ENGLISH_UNITED_STATES)
{
continue; /* Skip, try to get a more localized keyboard layout */
}
else if (current->keyboardLayouts[j] == 0)
{
/*
* If we skip the ENGLISH_UNITED_STATES keyboard layout but there are no
* other possible keyboard layout for the locale, we end up here with k > 1
*/
if (j >= 1)
return ENGLISH_UNITED_STATES;
/* No more keyboard layouts */
break;
}
else
return current->keyboardLayouts[j];
}
}
}
return -1;
}
const char* freerdp_get_system_locale_name_from_id(DWORD localeId)
{
for (size_t index = 0; index < ARRAYSIZE(LOCALE_NAME_TABLE); index++)
{
const LOCALE_NAME* current = &LOCALE_NAME_TABLE[index];
if (localeId == current->localeId)
return current->name;
}
return nullptr;
}
INT64 freerdp_get_locale_id_from_string(const char* locale)
{
const SYSTEM_LOCALE* loc = get_locale_from_str(locale);
if (!loc)
return -1;
return loc->code;
}
int freerdp_detect_keyboard_layout_from_system_locale(DWORD* keyboardLayoutId)
{
char language[LOCALE_LANGUAGE_LEN] = WINPR_C_ARRAY_INIT;
char country[LOCALE_COUNTRY_LEN] = WINPR_C_ARRAY_INIT;
freerdp_get_system_language_and_country_codes(language, ARRAYSIZE(language), country,
ARRAYSIZE(country));
char locale[LOCALE_COUNTRY_LEN + LOCALE_LANGUAGE_LEN + 2] = WINPR_C_ARRAY_INIT;
(void)_snprintf(locale, sizeof(locale), "%s_%s", language, country);
const int64_t id = freerdp_detect_keyboard_layout_from_locale(locale);
if (id < 0)
return -1;
*keyboardLayoutId = WINPR_ASSERTING_INT_CAST(uint32_t, id);
return 0;
}
int64_t freerdp_detect_keyboard_layout_from_locale(const char* localestr)
{
WINPR_ASSERT(localestr);
if ((strcmp(localestr, "C") == 0) || (strcmp(localestr, "POSIX") == 0))
return ENGLISH_UNITED_STATES; /* U.S. Keyboard Layout */
const SYSTEM_LOCALE* locale = get_locale_from_str(localestr);
if (!locale)
return -1;
DEBUG_KBD("Found locale : %s_%s", locale->language, locale->country);
return get_layout_from_locale(locale);
}
const SYSTEM_LOCALE* freerdp_get_system_locale_list(size_t* count)
{
WINPR_ASSERT(count);
*count = ARRAYSIZE(SYSTEM_LOCALE_TABLE);
return SYSTEM_LOCALE_TABLE;
}
DWORD freerdp_get_keyboard_default_layout_for_locale(DWORD locale)
{
for (size_t x = 0; x < ARRAYSIZE(LOCALE_KEYBOARD_LAYOUTS_TABLE); x++)
{
const LOCALE_KEYBOARD_LAYOUTS* cur = &LOCALE_KEYBOARD_LAYOUTS_TABLE[x];
if (cur->locale == locale)
return cur->keyboardLayouts[0];
}
return 0;
}

View File

@@ -0,0 +1,29 @@
# Test not compatible with package tests, disable
if(BUILD_TESTING_INTERNAL)
set(MODULE_NAME "TestLocale")
set(MODULE_PREFIX "TEST_LOCALE")
disable_warnings_for_directory(${CMAKE_CURRENT_BINARY_DIR})
set(DRIVER ${MODULE_NAME}.c)
set(TEST_SRCS TestLocaleKeyboard.c)
create_test_sourcelist(SRCS ${DRIVER} ${TEST_SRCS})
add_executable(${MODULE_NAME} ${SRCS})
add_compile_definitions(TESTING_OUTPUT_DIRECTORY="${PROJECT_BINARY_DIR}")
add_compile_definitions(TESTING_SRC_DIRECTORY="${PROJECT_SOURCE_DIR}")
target_link_libraries(${MODULE_NAME} freerdp winpr freerdp-client)
set_target_properties(${MODULE_NAME} PROPERTIES RUNTIME_OUTPUT_DIRECTORY "${TESTING_OUTPUT_DIRECTORY}")
foreach(test ${TEST_SRCS})
get_filename_component(TestName ${test} NAME_WE)
add_test(${TestName} ${TESTING_OUTPUT_DIRECTORY}/${MODULE_NAME} ${TestName})
endforeach()
set_property(TARGET ${MODULE_NAME} PROPERTY FOLDER "FreeRDP/Locale/Test")
endif()

View File

@@ -0,0 +1,405 @@
#include <stdio.h>
#include <winpr/crypto.h>
#include <freerdp/config.h>
#include <freerdp/locale/keyboard.h>
static BOOL test_scancode_name(void)
{
const DWORD scancodes[] = { RDP_SCANCODE_ESCAPE,
RDP_SCANCODE_KEY_1,
RDP_SCANCODE_KEY_2,
RDP_SCANCODE_KEY_3,
RDP_SCANCODE_KEY_4,
RDP_SCANCODE_KEY_5,
RDP_SCANCODE_KEY_6,
RDP_SCANCODE_KEY_7,
RDP_SCANCODE_KEY_8,
RDP_SCANCODE_KEY_9,
RDP_SCANCODE_KEY_0,
RDP_SCANCODE_OEM_MINUS,
RDP_SCANCODE_OEM_PLUS,
RDP_SCANCODE_BACKSPACE,
RDP_SCANCODE_TAB,
RDP_SCANCODE_KEY_Q,
RDP_SCANCODE_KEY_W,
RDP_SCANCODE_KEY_E,
RDP_SCANCODE_KEY_R,
RDP_SCANCODE_KEY_T,
RDP_SCANCODE_KEY_Y,
RDP_SCANCODE_KEY_U,
RDP_SCANCODE_KEY_I,
RDP_SCANCODE_KEY_O,
RDP_SCANCODE_KEY_P,
RDP_SCANCODE_OEM_4,
RDP_SCANCODE_OEM_6,
RDP_SCANCODE_RETURN,
RDP_SCANCODE_LCONTROL,
RDP_SCANCODE_KEY_A,
RDP_SCANCODE_KEY_S,
RDP_SCANCODE_KEY_D,
RDP_SCANCODE_KEY_F,
RDP_SCANCODE_KEY_G,
RDP_SCANCODE_KEY_H,
RDP_SCANCODE_KEY_J,
RDP_SCANCODE_KEY_K,
RDP_SCANCODE_KEY_L,
RDP_SCANCODE_OEM_1,
RDP_SCANCODE_OEM_7,
RDP_SCANCODE_OEM_3,
RDP_SCANCODE_LSHIFT,
RDP_SCANCODE_OEM_5,
RDP_SCANCODE_KEY_Z,
RDP_SCANCODE_KEY_X,
RDP_SCANCODE_KEY_C,
RDP_SCANCODE_KEY_V,
RDP_SCANCODE_KEY_B,
RDP_SCANCODE_KEY_N,
RDP_SCANCODE_KEY_M,
RDP_SCANCODE_OEM_COMMA,
RDP_SCANCODE_OEM_PERIOD,
RDP_SCANCODE_OEM_2,
RDP_SCANCODE_RSHIFT,
RDP_SCANCODE_MULTIPLY,
RDP_SCANCODE_LMENU,
RDP_SCANCODE_SPACE,
RDP_SCANCODE_CAPSLOCK,
RDP_SCANCODE_F1,
RDP_SCANCODE_F2,
RDP_SCANCODE_F3,
RDP_SCANCODE_F4,
RDP_SCANCODE_F5,
RDP_SCANCODE_F6,
RDP_SCANCODE_F7,
RDP_SCANCODE_F8,
RDP_SCANCODE_F9,
RDP_SCANCODE_F10,
RDP_SCANCODE_NUMLOCK,
RDP_SCANCODE_SCROLLLOCK,
RDP_SCANCODE_NUMPAD7,
RDP_SCANCODE_NUMPAD8,
RDP_SCANCODE_NUMPAD9,
RDP_SCANCODE_SUBTRACT,
RDP_SCANCODE_NUMPAD4,
RDP_SCANCODE_NUMPAD5,
RDP_SCANCODE_NUMPAD6,
RDP_SCANCODE_ADD,
RDP_SCANCODE_NUMPAD1,
RDP_SCANCODE_NUMPAD2,
RDP_SCANCODE_NUMPAD3,
RDP_SCANCODE_NUMPAD0,
RDP_SCANCODE_DECIMAL,
RDP_SCANCODE_SYSREQ,
RDP_SCANCODE_OEM_102,
RDP_SCANCODE_F11,
RDP_SCANCODE_F12,
RDP_SCANCODE_SLEEP,
RDP_SCANCODE_ZOOM,
RDP_SCANCODE_HELP,
RDP_SCANCODE_F13,
RDP_SCANCODE_F14,
RDP_SCANCODE_F15,
RDP_SCANCODE_F16,
RDP_SCANCODE_F17,
RDP_SCANCODE_F18,
RDP_SCANCODE_F19,
RDP_SCANCODE_F20,
RDP_SCANCODE_F21,
RDP_SCANCODE_F22,
RDP_SCANCODE_F23,
RDP_SCANCODE_F24,
RDP_SCANCODE_HIRAGANA,
RDP_SCANCODE_HANJA_KANJI,
RDP_SCANCODE_KANA_HANGUL,
RDP_SCANCODE_ABNT_C1,
RDP_SCANCODE_F24_JP,
RDP_SCANCODE_CONVERT_JP,
RDP_SCANCODE_NONCONVERT_JP,
RDP_SCANCODE_TAB_JP,
RDP_SCANCODE_BACKSLASH_JP,
RDP_SCANCODE_ABNT_C2,
RDP_SCANCODE_HANJA,
RDP_SCANCODE_HANGUL,
RDP_SCANCODE_RETURN_KP,
RDP_SCANCODE_RCONTROL,
RDP_SCANCODE_DIVIDE,
RDP_SCANCODE_PRINTSCREEN,
RDP_SCANCODE_RMENU,
RDP_SCANCODE_PAUSE,
RDP_SCANCODE_HOME,
RDP_SCANCODE_UP,
RDP_SCANCODE_PRIOR,
RDP_SCANCODE_LEFT,
RDP_SCANCODE_RIGHT,
RDP_SCANCODE_END,
RDP_SCANCODE_DOWN,
RDP_SCANCODE_NEXT,
RDP_SCANCODE_INSERT,
RDP_SCANCODE_DELETE,
RDP_SCANCODE_NULL,
RDP_SCANCODE_HELP2,
RDP_SCANCODE_LWIN,
RDP_SCANCODE_RWIN,
RDP_SCANCODE_APPS,
RDP_SCANCODE_POWER_JP,
RDP_SCANCODE_SLEEP_JP,
RDP_SCANCODE_NUMLOCK_EXTENDED,
RDP_SCANCODE_RSHIFT_EXTENDED,
RDP_SCANCODE_VOLUME_MUTE,
RDP_SCANCODE_VOLUME_DOWN,
RDP_SCANCODE_VOLUME_UP,
RDP_SCANCODE_MEDIA_NEXT_TRACK,
RDP_SCANCODE_MEDIA_PREV_TRACK,
RDP_SCANCODE_MEDIA_STOP,
RDP_SCANCODE_MEDIA_PLAY_PAUSE,
RDP_SCANCODE_BROWSER_BACK,
RDP_SCANCODE_BROWSER_FORWARD,
RDP_SCANCODE_BROWSER_REFRESH,
RDP_SCANCODE_BROWSER_STOP,
RDP_SCANCODE_BROWSER_SEARCH,
RDP_SCANCODE_BROWSER_FAVORITES,
RDP_SCANCODE_BROWSER_HOME,
RDP_SCANCODE_LAUNCH_MAIL,
RDP_SCANCODE_LAUNCH_MEDIA_SELECT,
RDP_SCANCODE_LAUNCH_APP1,
RDP_SCANCODE_LAUNCH_APP2 };
for (size_t x = 0; x < ARRAYSIZE(scancodes); x++)
{
const DWORD code = scancodes[x];
const char* sc = freerdp_keyboard_scancode_name(code);
if (!sc)
{
(void)fprintf(stderr, "Failed to run freerdp_keyboard_scancode_name(%" PRIu32 ")\n",
code);
return FALSE;
}
}
return TRUE;
}
static BOOL test_layouts(DWORD types)
{
BOOL rc = FALSE;
size_t count = 0;
RDP_KEYBOARD_LAYOUT* layouts = freerdp_keyboard_get_layouts(types, &count);
if (!layouts || (count == 0))
{
(void)fprintf(stderr,
"freerdp_keyboard_get_layouts(type: %" PRIu32 ") -> %" PRIuz
" elements, layouts: %p:\n",
types, count, layouts);
goto fail;
}
for (size_t x = 0; x < count; x++)
{
const RDP_KEYBOARD_LAYOUT* cur = &layouts[x];
if ((cur->code == 0) || (!cur->name) || (strnlen(cur->name, 2) == 0))
{
(void)fprintf(stderr,
"freerdp_keyboard_get_layouts(type: %" PRIu32 ") -> %" PRIuz
" elements, failed:\n",
types, count);
(void)fprintf(stderr, "[%" PRIuz "]: code= %" PRIu32 ", name = %s\n", x, cur->code,
cur->name);
goto fail;
}
const char* name = freerdp_keyboard_get_layout_name_from_id(cur->code);
if (!name)
{
(void)fprintf(stderr,
"freerdp_keyboard_get_layouts(type: %" PRIu32 ") -> %" PRIuz
" elements, failed:\n",
types, count);
(void)fprintf(stderr,
"[%" PRIuz "]: freerdp_keyboard_get_layouts(%" PRIu32 ") -> nullptr\n", x,
cur->code);
goto fail;
}
const DWORD id = freerdp_keyboard_get_layout_id_from_name(cur->name);
// if (id != cur->code) {
if (id == 0)
{
(void)fprintf(stderr,
"freerdp_keyboard_get_layouts(type: %" PRIu32 ") -> %" PRIuz
" elements, failed:\n",
types, count);
(void)fprintf(stderr,
"[%" PRIuz "]: freerdp_keyboard_get_layout_id_from_name(%s) -> %" PRIu32
" != %" PRIu32 "\n",
x, cur->name, id, cur->code);
goto fail;
}
}
rc = TRUE;
fail:
freerdp_keyboard_layouts_free(layouts, count);
return rc;
}
static DWORD get_random(DWORD offset)
{
DWORD x = 0;
if (winpr_RAND(&x, sizeof(x)) < 0)
{
(void)fprintf(stderr, "winpr_RAND failed, retry...\n");
// NOLINTNEXTLINE(concurrency-mt-unsafe)
exit(-1);
}
x = x % UINT32_MAX - offset;
x += offset;
return x;
}
#if !defined(WITHOUT_FREERDP_3x_DEPRECATED)
static BOOL test_scancode_cnv(void)
{
for (DWORD x = 0; x < UINT8_MAX; x++)
{
const DWORD sc = freerdp_keyboard_get_rdp_scancode_from_x11_keycode(x);
const BOOL ex = RDP_SCANCODE_EXTENDED(sc);
const DWORD kk = freerdp_keyboard_get_x11_keycode_from_rdp_scancode(sc, ex);
if (sc != kk)
{
(void)fprintf(stderr,
"[%" PRIu32 "]: keycode->scancode->keycode failed: %" PRIu32
" -> %" PRIu32 " -> %" PRIu32 "\n",
x, sc, ex, kk);
return FALSE;
}
}
for (DWORD x = 0; x < 23; x++)
{
DWORD x = get_random(UINT8_MAX);
const DWORD sc = freerdp_keyboard_get_rdp_scancode_from_x11_keycode(x);
const DWORD kk = freerdp_keyboard_get_x11_keycode_from_rdp_scancode(sc, FALSE);
const DWORD kkex = freerdp_keyboard_get_x11_keycode_from_rdp_scancode(sc, TRUE);
if ((sc != 0) || (kk != 0) || (kkex != 0))
{
(void)fprintf(stderr,
"[%" PRIu32 "]: invalid scancode %" PRIu32 ", keycode %" PRIu32
" or keycode extended %" PRIu32 " has a value != 0\n",
x, sc, kk, kkex);
return FALSE;
}
}
return TRUE;
}
#endif
static BOOL test_codepages(void)
{
for (DWORD column = 0; column < 4; column++)
{
size_t count = 0;
RDP_CODEPAGE* cp = freerdp_keyboard_get_matching_codepages(column, nullptr, &count);
if (!cp || (count == 0))
{
(void)fprintf(stderr,
"freerdp_keyboard_get_matching_codepages(%" PRIu32 ", nullptr) failed!\n",
column);
return FALSE;
}
freerdp_codepages_free(cp);
}
for (DWORD x = 0; x < 23; x++)
{
DWORD column = get_random(4);
size_t count = 0;
RDP_CODEPAGE* cp = freerdp_keyboard_get_matching_codepages(column, nullptr, &count);
freerdp_codepages_free(cp);
if (cp || (count != 0))
{
(void)fprintf(stderr,
"freerdp_keyboard_get_matching_codepages(%" PRIu32
", nullptr) returned not nullptr!\n",
column);
return FALSE;
}
}
// TODO: Test with filters set
// TODO: Test with invalid filters set
return TRUE;
}
static BOOL test_init(void)
{
#if !defined(WITHOUT_FREERDP_3x_DEPRECATED)
const DWORD kbd = freerdp_keyboard_init(0);
if (kbd == 0)
{
(void)fprintf(stderr, "freerdp_keyboard_init(0) returned invalid layout 0\n");
return FALSE;
}
const DWORD kbdex = freerdp_keyboard_init_ex(0, nullptr);
if (kbd == 0)
{
(void)fprintf(stderr, "freerdp_keyboard_init_ex(0, nullptr) returned invalid layout 0\n");
return FALSE;
}
if (kbd != kbdex)
{
(void)fprintf(
stderr,
"freerdp_keyboard_init(0) != freerdp_keyboard_init_ex(0, nullptr): returned %" PRIu32
" vs %" PRIu32 "\n",
kbd, kbdex);
return FALSE;
}
// TODO: Test with valid remap list
// TODO: Test with invalid remap list
// TODO: Test with defaults != 0
#endif
return TRUE;
}
int TestLocaleKeyboard(int argc, char* argv[])
{
WINPR_UNUSED(argc);
WINPR_UNUSED(argv);
if (!test_scancode_name())
return -1;
if (!test_layouts(RDP_KEYBOARD_LAYOUT_TYPE_STANDARD))
return -1;
if (!test_layouts(RDP_KEYBOARD_LAYOUT_TYPE_VARIANT))
return -1;
if (!test_layouts(RDP_KEYBOARD_LAYOUT_TYPE_IME))
return -1;
if (!test_layouts(RDP_KEYBOARD_LAYOUT_TYPE_STANDARD | RDP_KEYBOARD_LAYOUT_TYPE_VARIANT))
return -1;
if (!test_layouts(RDP_KEYBOARD_LAYOUT_TYPE_STANDARD | RDP_KEYBOARD_LAYOUT_TYPE_IME))
return -1;
if (!test_layouts(RDP_KEYBOARD_LAYOUT_TYPE_VARIANT | RDP_KEYBOARD_LAYOUT_TYPE_IME))
return -1;
if (!test_layouts(RDP_KEYBOARD_LAYOUT_TYPE_STANDARD | RDP_KEYBOARD_LAYOUT_TYPE_VARIANT |
RDP_KEYBOARD_LAYOUT_TYPE_IME))
return -1;
if (test_layouts(UINT32_MAX &
~(RDP_KEYBOARD_LAYOUT_TYPE_STANDARD | RDP_KEYBOARD_LAYOUT_TYPE_VARIANT |
RDP_KEYBOARD_LAYOUT_TYPE_IME)))
return -1;
#if !defined(WITHOUT_FREERDP_3x_DEPRECATED)
if (!test_scancode_cnv())
return -1;
#endif
if (!test_codepages())
return -1;
if (!test_init())
return -1;
return 0;
}

View File

@@ -0,0 +1,871 @@
/**
* FreeRDP: A Remote Desktop Protocol Implementation
* RDP Keyboard layout ID detection from common X11 xkb keyboard layout names
*
* Copyright 2009-2012 Marc-Andre Moreau <marcandre.moreau@gmail.com>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <freerdp/config.h>
#include "xkb_layout_ids.h"
#include <string.h>
#include <winpr/crt.h>
#include <freerdp/locale/keyboard.h>
#include "liblocale.h"
typedef struct
{
const char* variant; /* XKB Keyboard layout variant */
INT64 keyboardLayoutID; /* Keyboard Layout ID */
} XKB_VARIANT;
typedef struct
{
const char* layout; /* XKB Keyboard layout */
INT64 keyboardLayoutID; /* Keyboard Layout ID */
const XKB_VARIANT* variants;
} XKB_LAYOUT;
/* Those have been generated automatically and are waiting to be filled by hand */
/* USA */
static const XKB_VARIANT us_variants[] = {
{ "chr", 0 }, /* Cherokee */
{ "euro", 0 }, /* With EuroSign on 5 */
{ "intl", KBD_UNITED_STATES_INTERNATIONAL }, /* International (with dead keys) */
{ "alt-intl",
KBD_UNITED_STATES_INTERNATIONAL }, /* Alternative international (former us_intl) */
{ "colemak", 0 }, /* Colemak */
{ "dvorak", KBD_UNITED_STATES_DVORAK }, /* Dvorak */
{ "dvorak-intl", KBD_UNITED_STATES_DVORAK }, /* Dvorak international */
{ "dvorak-l", KBD_UNITED_STATES_DVORAK_FOR_LEFT_HAND }, /* Left handed Dvorak */
{ "dvorak-r", KBD_UNITED_STATES_DVORAK_FOR_RIGHT_HAND }, /* Right handed Dvorak */
{ "dvorak-classic", KBD_UNITED_STATES_DVORAK }, /* Classic Dvorak */
{ "dvp", KBD_UNITED_STATES_DVORAK_PROGRAMMER }, /* Programmer Dvorak */
{ "rus", 0 }, /* Russian phonetic */
{ "mac", KBD_US }, /* Macintosh */
{ "altgr-intl", KBD_UNITED_STATES_INTERNATIONAL }, /* International (AltGr dead keys) */
{ "olpc2", KBD_US }, /* Group toggle on multiply/divide key */
{ "", 0 },
};
/* Afghanistan */
static const XKB_VARIANT af_variants[] = {
{ "ps", KBD_PASHTO }, /* Pashto */
{ "uz", KBD_UZBEK_CYRILLIC }, /* Southern Uzbek */
{ "olpc-ps", KBD_PASHTO }, /* OLPC Pashto */
{ "olpc-fa", 0 }, /* OLPC Dari */
{ "olpc-uz", KBD_UZBEK_CYRILLIC }, /* OLPC Southern Uzbek */
{ "", 0 },
};
/* Arabic */
static const XKB_VARIANT ara_variants[] = {
{ "azerty", KBD_ARABIC_102_AZERTY }, /* azerty */
{ "azerty_digits", KBD_ARABIC_102_AZERTY }, /* azerty/digits */
{ "digits", KBD_ARABIC_102_AZERTY }, /* digits */
{ "qwerty", KBD_ARABIC_101 }, /* qwerty */
{ "qwerty_digits", KBD_ARABIC_101 }, /* qwerty/digits */
{ "buckwalter", KBD_US_ENGLISH_TABLE_FOR_IBM_ARABIC_238_L }, /* Buckwalter */
{ "", 0 },
};
/* Armenia */
static const XKB_VARIANT am_variants[] = {
{ "phonetic", 0 }, /* Phonetic */
{ "phonetic-alt", 0 }, /* Alternative Phonetic */
{ "eastern", KBD_ARMENIAN_EASTERN }, /* Eastern */
{ "western", KBD_ARMENIAN_WESTERN }, /* Western */
{ "eastern-alt", KBD_ARMENIAN_EASTERN }, /* Alternative Eastern */
{ "", 0 },
};
/* Azerbaijan */
static const XKB_VARIANT az_variants[] = {
{ "cyrillic", KBD_AZERI_CYRILLIC }, /* Cyrillic */
{ "", 0 },
};
/* Belarus */
static const XKB_VARIANT by_variants[] = {
{ "winkeys", KBD_BELARUSIAN }, /* Winkeys */
{ "latin", KBD_BELARUSIAN }, /* Latin */
{ "", 0 },
};
/* Belgium */
static const XKB_VARIANT be_variants[] = {
{ "oss", KBD_BELGIAN_FRENCH }, /* Alternative */
{ "oss_latin9", KBD_BELGIAN_FRENCH }, /* Alternative, latin-9 only */
{ "oss_sundeadkeys", KBD_BELGIAN_PERIOD }, /* Alternative, Sun dead keys */
{ "iso-alternate", KBD_BELGIAN_COMMA }, /* ISO Alternate */
{ "nodeadkeys", KBD_BELGIAN_COMMA }, /* Eliminate dead keys */
{ "sundeadkeys", KBD_BELGIAN_PERIOD }, /* Sun dead keys */
{ "wang", KBD_BELGIAN_FRENCH }, /* Wang model 724 azerty */
{ "", 0 },
};
/* Bangladesh */
static const XKB_VARIANT bd_variants[] = {
{ "probhat", KBD_BENGALI_INSCRIPT }, /* Probhat */
{ "", 0 },
};
/* India */
static const XKB_VARIANT in_variants[] = {
{ "ben", KBD_BENGALI }, /* Bengali */
{ "ben_probhat", KBD_BENGALI_INSCRIPT }, /* Bengali Probhat */
{ "guj", KBD_GUJARATI }, /* Gujarati */
{ "guru", 0 }, /* Gurmukhi */
{ "jhelum", 0 }, /* Gurmukhi Jhelum */
{ "kan", KBD_KANNADA }, /* Kannada */
{ "mal", KBD_MALAYALAM }, /* Malayalam */
{ "mal_lalitha", KBD_MALAYALAM }, /* Malayalam Lalitha */
{ "ori", 0 }, /* Oriya */
{ "tam_unicode", KBD_TAMIL }, /* Tamil Unicode */
{ "tam_TAB", KBD_TAMIL }, /* Tamil TAB Typewriter */
{ "tam_TSCII", KBD_TAMIL }, /* Tamil TSCII Typewriter */
{ "tam", KBD_TAMIL }, /* Tamil */
{ "tel", KBD_TELUGU }, /* Telugu */
{ "urd-phonetic", KBD_URDU }, /* Urdu, Phonetic */
{ "urd-phonetic3", KBD_URDU }, /* Urdu, Alternative phonetic */
{ "urd-winkeys", KBD_URDU }, /* Urdu, Winkeys */
{ "bolnagri", KBD_HINDI_TRADITIONAL }, /* Hindi Bolnagri */
{ "hin-wx", KBD_HINDI_TRADITIONAL }, /* Hindi Wx */
{ "", 0 },
};
/* Bosnia and Herzegovina */
static const XKB_VARIANT ba_variants[] = {
{ "alternatequotes", KBD_BOSNIAN }, /* Use guillemets for quotes */
{ "unicode", KBD_BOSNIAN }, /* Use Bosnian digraphs */
{ "unicodeus", KBD_BOSNIAN }, /* US keyboard with Bosnian digraphs */
{ "us", KBD_BOSNIAN_CYRILLIC }, /* US keyboard with Bosnian letters */
{ "", 0 },
};
/* Brazil */
static const XKB_VARIANT br_variants[] = {
{ "nodeadkeys", KBD_PORTUGUESE_BRAZILIAN_ABNT2 }, /* Eliminate dead keys */
{ "dvorak", KBD_UNITED_STATES_DVORAK }, /* Dvorak */
{ "nativo", KBD_PORTUGUESE_BRAZILIAN_ABNT2 }, /* Nativo */
{ "nativo-us", KBD_PORTUGUESE_BRAZILIAN_ABNT2 }, /* Nativo for USA keyboards */
{ "nativo-epo", KBD_PORTUGUESE_BRAZILIAN_ABNT2 }, /* Nativo for Esperanto */
{ "", 0 },
};
/* Bulgaria */
static const XKB_VARIANT bg_variants[] = {
{ "phonetic", KBD_BULGARIAN_LATIN }, /* Traditional Phonetic */
{ "bas_phonetic", KBD_BULGARIAN_LATIN }, /* Standard Phonetic */
{ "", 0 },
};
/* Morocco */
static const XKB_VARIANT ma_variants[] = {
{ "french", KBD_FRENCH }, /* French */
{ "tifinagh", 0 }, /* Tifinagh */
{ "tifinagh-alt", 0 }, /* Tifinagh Alternative */
{ "tifinagh-alt-phonetic", 0 }, /* Tifinagh Alternative Phonetic */
{ "tifinagh-extended", 0 }, /* Tifinagh Extended */
{ "tifinagh-phonetic", 0 }, /* Tifinagh Phonetic */
{ "tifinagh-extended-phonetic", 0 }, /* Tifinagh Extended Phonetic */
{ "", 0 },
};
/* Canada */
static const XKB_VARIANT ca_variants[] = {
{ "fr", KBD_CANADIAN_FRENCH }, /* French Dvorak */
{ "fr-dvorak", KBD_UNITED_STATES_DVORAK }, /* French Dvorak */
{ "fr-legacy", KBD_CANADIAN_FRENCH_LEGACY }, /* French (legacy) */
{ "multix", KBD_CANADIAN_MULTILINGUAL_STANDARD }, /* Multilingual */
{ "multi", KBD_CANADIAN_MULTILINGUAL_STANDARD }, /* Multilingual, first part */
{ "multi-2gr", KBD_CANADIAN_MULTILINGUAL_STANDARD }, /* Multilingual, second part */
{ "ike", KBD_INUKTITUT_LATIN }, /* Inuktitut */
{ "shs" /* codespell:ignore shs */, 0 }, /* Secwepemctsin */
{ "kut", 0 }, /* Ktunaxa */
{ "", 0 },
};
/* China */
static const XKB_VARIANT cn_variants[] = {
{ "tib", 0 }, /* Tibetan */
{ "tib_asciinum", 0 }, /* Tibetan (with ASCII numerals) */
{ "", 0 },
};
/* Croatia */
static const XKB_VARIANT hr_variants[] = {
{ "alternatequotes", KBD_CROATIAN }, /* Use guillemets for quotes */
{ "unicode", KBD_CROATIAN }, /* Use Croatian digraphs */
{ "unicodeus", KBD_CROATIAN }, /* US keyboard with Croatian digraphs */
{ "us", KBD_CROATIAN }, /* US keyboard with Croatian letters */
{ "", 0 },
};
/* Czechia */
static const XKB_VARIANT cz_variants[] = {
{ "bksl", KBD_CZECH_PROGRAMMERS }, /* With &lt;\|&gt; key */
{ "qwerty", KBD_CZECH_QWERTY }, /* qwerty */
{ "qwerty_bksl", KBD_CZECH_QWERTY }, /* qwerty, extended Backslash */
{ "ucw", KBD_CZECH }, /* UCW layout (accented letters only) */
{ "", 0 },
};
/* Denmark */
static const XKB_VARIANT dk_variants[] = {
{ "nodeadkeys", KBD_DANISH }, /* Eliminate dead keys */
{ "mac", KBD_DANISH }, /* Macintosh */
{ "mac_nodeadkeys", KBD_DANISH }, /* Macintosh, eliminate dead keys */
{ "dvorak", KBD_UNITED_STATES_DVORAK }, /* Dvorak */
{ "", 0 },
};
/* Netherlands */
static const XKB_VARIANT nl_variants[] = {
{ "sundeadkeys", KBD_SWISS_FRENCH }, /* Sun dead keys */
{ "mac", KBD_SWISS_FRENCH }, /* Macintosh */
{ "std", KBD_SWISS_FRENCH }, /* Standard */
{ "", 0 },
};
/* Estonia */
static const XKB_VARIANT ee_variants[] = {
{ "nodeadkeys", KBD_US }, /* Eliminate dead keys */
{ "dvorak", KBD_UNITED_STATES_DVORAK }, /* Dvorak */
{ "us", KBD_UNITED_STATES_INTERNATIONAL }, /* US keyboard with Estonian letters */
{ "", 0 },
};
/* Iran */
static const XKB_VARIANT ir_variants[] = {
{ "pro", 0 }, /* Pro */
{ "keypad", 0 }, /* Keypad */
{ "pro_keypad", 0 }, /* Pro Keypad */
{ "ku", 0 }, /* Kurdish, Latin Q */
{ "ku_f", 0 }, /* Kurdish, (F) */
{ "ku_alt", 0 }, /* Kurdish, Latin Alt-Q */
{ "ku_ara", 0 }, /* Kurdish, Arabic-Latin */
{ "", 0 },
};
/* Iraq */
static const XKB_VARIANT iq_variants[] = {
{ "ku", 0 }, /* Kurdish, Latin Q */
{ "ku_f", 0 }, /* Kurdish, (F) */
{ "ku_alt", 0 }, /* Kurdish, Latin Alt-Q */
{ "ku_ara", 0 }, /* Kurdish, Arabic-Latin */
{ "", 0 },
};
/* Faroe Islands */
static const XKB_VARIANT fo_variants[] = {
{ "nodeadkeys", 0 }, /* Eliminate dead keys */
{ "", 0 },
};
/* Finland */
static const XKB_VARIANT fi_variants[] = {
{ "nodeadkeys", 0 }, /* Eliminate dead keys */
{ "smi", 0 }, /* Northern Saami */
{ "classic", 0 }, /* Classic */
{ "mac", 0 }, /* Macintosh */
{ "", 0 },
};
/* France */
static const XKB_VARIANT fr_variants[] = {
{ "nodeadkeys", 0 }, /* Eliminate dead keys */
{ "sundeadkeys", 0 }, /* Sun dead keys */
{ "oss", 0 }, /* Alternative */
{ "oss_latin9", 0 }, /* Alternative, latin-9 only */
{ "oss_nodeadkeys", 0 }, /* Alternative, eliminate dead keys */
{ "oss_sundeadkeys", 0 }, /* Alternative, Sun dead keys */
{ "latin9", 0 }, /* (Legacy) Alternative */
{ "latin9_nodeadkeys", 0 }, /* (Legacy) Alternative, eliminate dead keys */
{ "latin9_sundeadkeys", 0 }, /* (Legacy) Alternative, Sun dead keys */
{ "bepo", KBD_FRENCH_BEPO }, /* Bepo, ergonomic, Dvorak way */
{ "bepo_latin9", 0 }, /* Bepo, ergonomic, Dvorak way, latin-9 only */
{ "dvorak", 0 }, /* Dvorak */
{ "mac", 0 }, /* Macintosh */
{ "bre", 0 }, /* Breton */
{ "oci", 0 }, /* Occitan */
{ "geo", 0 }, /* Georgian AZERTY Tskapo */
{ "", 0 },
};
/* Ghana */
static const XKB_VARIANT gh_variants[] = {
{ "generic", 0 }, /* Multilingual */
{ "akan", 0 }, /* Akan */
{ "ewe", 0 }, /* Ewe */
{ "fula", 0 }, /* Fula */
{ "ga", 0 }, /* Ga */
{ "hausa", 0 }, /* Hausa */
{ "", 0 },
};
/* Georgia */
static const XKB_VARIANT ge_variants[] = {
{ "ergonomic", 0 }, /* Ergonomic */
{ "mess", 0 }, /* MESS */
{ "ru", 0 }, /* Russian */
{ "os", 0 }, /* Ossetian */
{ "", 0 },
};
/* Germany */
static const XKB_VARIANT de_variants[] = {
{ "deadacute", KBD_GERMAN }, /* Dead acute */
{ "deadgraveacute", KBD_GERMAN }, /* Dead grave acute */
{ "nodeadkeys", KBD_GERMAN }, /* Eliminate dead keys */
{ "ro", KBD_GERMAN }, /* Romanian keyboard with German letters */
{ "ro_nodeadkeys",
KBD_GERMAN }, /* Romanian keyboard with German letters, eliminate dead keys */
{ "dvorak", KBD_UNITED_STATES_DVORAK }, /* Dvorak */
{ "sundeadkeys", KBD_GERMAN }, /* Sun dead keys */
{ "neo", KBD_GERMAN_NEO }, /* Neo 2 */
{ "mac", KBD_GERMAN }, /* Macintosh */
{ "mac_nodeadkeys", KBD_GERMAN }, /* Macintosh, eliminate dead keys */
{ "dsb", KBD_GERMAN }, /* Lower Sorbian */
{ "dsb_qwertz", KBD_GERMAN }, /* Lower Sorbian (qwertz) */
{ "qwerty", KBD_GERMAN_IBM }, /* qwerty */
{ "", 0 },
};
/* Greece */
static const XKB_VARIANT gr_variants[] = {
{ "simple", KBD_GREEK_220 }, /* Simple */
{ "extended", KBD_GREEK_319 }, /* Extended */
{ "nodeadkeys", KBD_GREEK_319 }, /* Eliminate dead keys */
{ "polytonic", KBD_GREEK_POLYTONIC }, /* Polytonic */
{ "", 0 },
};
/* Hungary */
static const XKB_VARIANT hu_variants[] = {
{ "standard", KBD_HUNGARIAN_101_KEY }, /* Standard */
{ "nodeadkeys", KBD_HUNGARIAN_101_KEY }, /* Eliminate dead keys */
{ "qwerty", KBD_HUNGARIAN_101_KEY }, /* qwerty */
{ "101_qwertz_comma_dead", KBD_HUNGARIAN_101_KEY }, /* 101/qwertz/comma/Dead keys */
{ "101_qwertz_comma_nodead", KBD_HUNGARIAN_101_KEY }, /* 101/qwertz/comma/Eliminate dead keys */
{ "101_qwertz_dot_dead", KBD_HUNGARIAN_101_KEY }, /* 101/qwertz/dot/Dead keys */
{ "101_qwertz_dot_nodead", KBD_HUNGARIAN_101_KEY }, /* 101/qwertz/dot/Eliminate dead keys */
{ "101_qwerty_comma_dead", KBD_HUNGARIAN_101_KEY }, /* 101/qwerty/comma/Dead keys */
{ "101_qwerty_comma_nodead", KBD_HUNGARIAN_101_KEY }, /* 101/qwerty/comma/Eliminate dead keys */
{ "101_qwerty_dot_dead", KBD_HUNGARIAN_101_KEY }, /* 101/qwerty/dot/Dead keys */
{ "101_qwerty_dot_nodead", KBD_HUNGARIAN_101_KEY }, /* 101/qwerty/dot/Eliminate dead keys */
{ "102_qwertz_comma_dead", KBD_HUNGARIAN_101_KEY }, /* 102/qwertz/comma/Dead keys */
{ "102_qwertz_comma_nodead", KBD_HUNGARIAN_101_KEY }, /* 102/qwertz/comma/Eliminate dead keys */
{ "102_qwertz_dot_dead", KBD_HUNGARIAN_101_KEY }, /* 102/qwertz/dot/Dead keys */
{ "102_qwertz_dot_nodead", KBD_HUNGARIAN_101_KEY }, /* 102/qwertz/dot/Eliminate dead keys */
{ "102_qwerty_comma_dead", KBD_HUNGARIAN_101_KEY }, /* 102/qwerty/comma/Dead keys */
{ "102_qwerty_comma_nodead", KBD_HUNGARIAN_101_KEY }, /* 102/qwerty/comma/Eliminate dead keys */
{ "102_qwerty_dot_dead", KBD_HUNGARIAN_101_KEY }, /* 102/qwerty/dot/Dead keys */
{ "102_qwerty_dot_nodead", KBD_HUNGARIAN_101_KEY }, /* 102/qwerty/dot/Eliminate dead keys */
{ "", 0 },
};
/* Iceland */
static const XKB_VARIANT is_variants[] = {
{ "Sundeadkeys", KBD_ICELANDIC }, /* Sun dead keys */
{ "nodeadkeys", KBD_ICELANDIC }, /* Eliminate dead keys */
{ "mac", KBD_ICELANDIC }, /* Macintosh */
{ "dvorak", KBD_UNITED_STATES_DVORAK }, /* Dvorak */
{ "", 0 },
};
/* Israel */
static const XKB_VARIANT il_variants[] = {
{ "lyx", KBD_HEBREW }, /* lyx */
{ "phonetic", KBD_HEBREW }, /* Phonetic */
{ "biblical", KBD_HEBREW }, /* Biblical Hebrew (Tiro) */
{ "", 0 },
};
/* Italy */
static const XKB_VARIANT it_variants[] = {
{ "nodeadkeys", KBD_ITALIAN_142 }, /* Eliminate dead keys */
{ "mac", KBD_ITALIAN }, /* Macintosh */
{ "geo", KBD_GEORGIAN }, /* Georgian */
{ "", 0 },
};
/* Japan */
static const XKB_VARIANT jp_variants[] = {
{ "kana", KBD_JAPANESE_INPUT_SYSTEM_MS_IME2002 }, /* Kana */
{ "OADG109A", KBD_JAPANESE_INPUT_SYSTEM_MS_IME2002 }, /* OADG 109A */
{ "", 0 },
};
/* Kyrgyzstan */
static const XKB_VARIANT kg_variants[] = {
{ "phonetic", KBD_KYRGYZ_CYRILLIC }, /* Phonetic */
{ "", 0 },
};
/* Kazakhstan */
static const XKB_VARIANT kz_variants[] = {
{ "ruskaz", KBD_KAZAKH }, /* Russian with Kazakh */
{ "kazrus", KBD_KAZAKH }, /* Kazakh with Russian */
{ "", 0 },
};
/* Latin America */
static const XKB_VARIANT latam_variants[] = {
{ "nodeadkeys", KBD_LATIN_AMERICAN }, /* Eliminate dead keys */
{ "deadtilde", KBD_LATIN_AMERICAN }, /* Include dead tilde */
{ "sundeadkeys", KBD_LATIN_AMERICAN }, /* Sun dead keys */
{ "", 0 },
};
/* Lithuania */
static const XKB_VARIANT lt_variants[] = {
{ "std", KBD_LITHUANIAN }, /* Standard */
{ "us", KBD_LITHUANIAN_IBM }, /* US keyboard with Lithuanian letters */
{ "ibm", KBD_LITHUANIAN_IBM }, /* IBM (LST 1205-92) */
{ "lekp", KBD_LITHUANIAN }, /* LEKP */
{ "lekpa", KBD_LITHUANIAN }, /* LEKPa */
{ "balticplus", KBD_LITHUANIAN }, /* Baltic+ */
{ "", 0 },
};
/* Latvia */
static const XKB_VARIANT lv_variants[] = {
{ "apostrophe", KBD_LATVIAN }, /* Apostrophe (') variant */
{ "tilde", KBD_LATVIAN }, /* Tilde (~) variant */
{ "fkey", KBD_LATVIAN }, /* F-letter (F) variant */
{ "", 0 },
};
/* Montenegro */
static const XKB_VARIANT me_variants[] = {
{ "cyrillic", 0 }, /* Cyrillic */
{ "cyrillicyz", 0 }, /* Cyrillic, Z and ZHE swapped */
{ "latinunicode", 0 }, /* Latin unicode */
{ "latinyz", 0 }, /* Latin qwerty */
{ "latinunicodeyz", 0 }, /* Latin unicode qwerty */
{ "cyrillicalternatequotes", 0 }, /* Cyrillic with guillemets */
{ "latinalternatequotes", 0 }, /* Latin with guillemets */
{ "", 0 },
};
/* Macedonia */
static const XKB_VARIANT mk_variants[] = {
{ "nodeadkeys", KBD_FYRO_MACEDONIAN }, /* Eliminate dead keys */
{ "", 0 },
};
/* Malta */
static const XKB_VARIANT mt_variants[] = {
{ "us", KBD_MALTESE_48_KEY }, /* Maltese keyboard with US layout */
{ "", 0 },
};
/* Norway */
static const XKB_VARIANT no_variants[] = {
{ "nodeadkeys", KBD_NORWEGIAN }, /* Eliminate dead keys */
{ "dvorak", KBD_UNITED_STATES_DVORAK }, /* Dvorak */
{ "smi", KBD_NORWEGIAN_WITH_SAMI }, /* Northern Saami */
{ "smi_nodeadkeys", KBD_SAMI_EXTENDED_NORWAY }, /* Northern Saami, eliminate dead keys */
{ "mac", KBD_NORWEGIAN }, /* Macintosh */
{ "mac_nodeadkeys", KBD_SAMI_EXTENDED_NORWAY }, /* Macintosh, eliminate dead keys */
{ "", 0 },
};
/* Poland */
static const XKB_VARIANT pl_variants[] = {
{ "qwertz", KBD_POLISH_214 }, /* qwertz */
{ "dvorak", KBD_UNITED_STATES_DVORAK }, /* Dvorak */
{ "dvorak_quotes", KBD_UNITED_STATES_DVORAK }, /* Dvorak, Polish quotes on quotemark key */
{ "dvorak_altquotes", KBD_UNITED_STATES_DVORAK }, /* Dvorak, Polish quotes on key 1 */
{ "csb", 0 }, /* Kashubian */
{ "ru_phonetic_dvorak", KBD_UNITED_STATES_DVORAK }, /* Russian phonetic Dvorak */
{ "", 0 },
};
/* Portugal */
static const XKB_VARIANT pt_variants[] = {
{ "nodeadkeys", KBD_PORTUGUESE }, /* Eliminate dead keys */
{ "sundeadkeys", KBD_PORTUGUESE }, /* Sun dead keys */
{ "mac", KBD_PORTUGUESE }, /* Macintosh */
{ "mac_nodeadkeys", KBD_PORTUGUESE }, /* Macintosh, eliminate dead keys */
{ "mac_sundeadkeys", KBD_PORTUGUESE }, /* Macintosh, Sun dead keys */
{ "nativo", KBD_PORTUGUESE }, /* Nativo */
{ "nativo-us", KBD_PORTUGUESE }, /* Nativo for USA keyboards */
{ "nativo-epo", KBD_PORTUGUESE }, /* Nativo for Esperanto */
{ "", 0 },
};
/* Romania */
static const XKB_VARIANT ro_variants[] = {
{ "cedilla", KBD_ROMANIAN }, /* Cedilla */
{ "std", KBD_ROMANIAN }, /* Standard */
{ "std_cedilla", KBD_ROMANIAN }, /* Standard (Cedilla) */
{ "winkeys", KBD_ROMANIAN }, /* Winkeys */
{ "crh_f", KBD_TURKISH_F }, /* Crimean Tatar (Turkish F) */
{ "crh_alt", KBD_TURKISH_Q }, /* Crimean Tatar (Turkish Alt-Q) */
{ "crh_dobruca1", KBD_TATAR }, /* Crimean Tatar (Dobruca-1 Q) */
{ "crh_dobruca2", KBD_TATAR }, /* Crimean Tatar (Dobruca-2 Q) */
{ "", 0 },
};
/* Russia */
static const XKB_VARIANT ru_variants[] = {
{ "phonetic", KBD_RUSSIAN }, /* Phonetic */
{ "phonetic_winkeys", KBD_RUSSIAN }, /* Phonetic Winkeys */
{ "typewriter", KBD_RUSSIAN_TYPEWRITER }, /* Typewriter */
{ "legacy", KBD_RUSSIAN }, /* Legacy */
{ "tt", KBD_TATAR }, /* Tatar */
{ "os_legacy", 0 }, /* Ossetian, legacy */
{ "os_winkeys", 0 }, /* Ossetian, Winkeys */
{ "cv", 0 }, /* Chuvash */
{ "cv_latin", 0 }, /* Chuvash Latin */
{ "udm", 0 }, /* Udmurt */
{ "kom", 0 }, /* Komi */
{ "sah", 0 }, /* Yakut */
{ "xal", 0 }, /* Kalmyk */
{ "dos", 0 }, /* DOS */
{ "", 0 },
};
/* Serbia */
static const XKB_VARIANT rs_variants[] = {
{ "yz", KBD_SERBIAN_CYRILLIC }, /* Z and ZHE swapped */
{ "latin", KBD_SERBIAN_LATIN }, /* Latin */
{ "latinunicode", KBD_SERBIAN_LATIN }, /* Latin Unicode */
{ "latinyz", KBD_SERBIAN_LATIN }, /* Latin qwerty */
{ "latinunicodeyz", KBD_SERBIAN_LATIN }, /* Latin Unicode qwerty */
{ "alternatequotes", KBD_SERBIAN_CYRILLIC }, /* With guillemets */
{ "latinalternatequotes", KBD_SERBIAN_LATIN }, /* Latin with guillemets */
{ "", 0 },
};
/* Slovenia */
static const XKB_VARIANT si_variants[] = {
{ "alternatequotes", KBD_SLOVENIAN }, /* Use guillemets for quotes */
{ "us", KBD_UNITED_STATES_INTERNATIONAL }, /* US keyboard with Slovenian letters */
{ "", 0 },
};
/* Slovakia */
static const XKB_VARIANT sk_variants[] = {
{ "bksl", KBD_SLOVAK }, /* Extended Backslash */
{ "qwerty", KBD_SLOVAK_QWERTY }, /* qwerty */
{ "qwerty_bksl", KBD_SLOVAK_QWERTY }, /* qwerty, extended Backslash */
{ "", 0 },
};
/* Spain */
static const XKB_VARIANT es_variants[] = {
{ "nodeadkeys", KBD_SPANISH_VARIATION }, /* Eliminate dead keys */
{ "deadtilde", KBD_SPANISH_VARIATION }, /* Include dead tilde */
{ "sundeadkeys", KBD_SPANISH }, /* Sun dead keys */
{ "dvorak", KBD_UNITED_STATES_DVORAK }, /* Dvorak */
{ "ast", KBD_SPANISH_VARIATION }, /* Asturian variant with bottom-dot H and bottom-dot L */
{ "cat", KBD_SPANISH_VARIATION }, /* Catalan variant with middle-dot L */
{ "mac", KBD_SPANISH }, /* Macintosh */
{ "", 0 },
};
/* Sweden */
static const XKB_VARIANT se_variants[] = {
{ "nodeadkeys", KBD_SWEDISH }, /* Eliminate dead keys */
{ "dvorak", KBD_UNITED_STATES_DVORAK }, /* Dvorak */
{ "rus", KBD_RUSSIAN }, /* Russian phonetic */
{ "rus_nodeadkeys", KBD_RUSSIAN }, /* Russian phonetic, eliminate dead keys */
{ "smi", KBD_SWEDISH_WITH_SAMI }, /* Northern Saami */
{ "mac", KBD_SWEDISH }, /* Macintosh */
{ "svdvorak", KBD_UNITED_STATES_DVORAK }, /* Svdvorak */
{ "", 0 },
};
/* Switzerland */
static const XKB_VARIANT ch_variants[] = {
{ "de_nodeadkeys", KBD_SWISS_GERMAN }, /* German, eliminate dead keys */
{ "de_sundeadkeys", KBD_SWISS_GERMAN }, /* German, Sun dead keys */
{ "fr", KBD_SWISS_FRENCH }, /* French */
{ "fr_nodeadkeys", KBD_SWISS_FRENCH }, /* French, eliminate dead keys */
{ "fr_sundeadkeys", KBD_SWISS_FRENCH }, /* French, Sun dead keys */
{ "fr_mac", KBD_SWISS_FRENCH }, /* French (Macintosh) */
{ "de_mac", KBD_SWISS_GERMAN }, /* German (Macintosh) */
{ "", 0 },
};
/* Syria */
static const XKB_VARIANT sy_variants[] = {
{ "syc", KBD_SYRIAC }, /* Syriac */
{ "syc_phonetic", KBD_SYRIAC_PHONETIC }, /* Syriac phonetic */
{ "ku", 0 }, /* Kurdish, Latin Q */
{ "ku_f", 0 }, /* Kurdish, (F) */
{ "ku_alt", 0 }, /* Kurdish, Latin Alt-Q */
{ "", 0 },
};
/* Tajikistan */
static const XKB_VARIANT tj_variants[] = {
{ "legacy", 0 }, /* Legacy */
{ "", 0 },
};
/* Sri Lanka */
static const XKB_VARIANT lk_variants[] = {
{ "tam_unicode", KBD_TAMIL }, /* Tamil Unicode */
{ "tam_TAB", KBD_TAMIL }, /* Tamil TAB Typewriter */
{ "", 0 },
};
/* Thailand */
static const XKB_VARIANT th_variants[] = {
{ "tis", KBD_THAI_KEDMANEE_NON_SHIFTLOCK }, /* TIS-820.2538 */
{ "pat", KBD_THAI_PATTACHOTE }, /* Pattachote */
{ "", 0 },
};
/* Turkey */
static const XKB_VARIANT tr_variants[] = {
{ "f", KBD_TURKISH_F }, /* (F) */
{ "alt", KBD_TURKISH_Q }, /* Alt-Q */
{ "sundeadkeys", KBD_TURKISH_F }, /* Sun dead keys */
{ "ku", 0 }, /* Kurdish, Latin Q */
{ "ku_f", 0 }, /* Kurdish, (F) */
{ "ku_alt", 0 }, /* Kurdish, Latin Alt-Q */
{ "intl", KBD_TURKISH_F }, /* International (with dead keys) */
{ "crh", KBD_TATAR }, /* Crimean Tatar (Turkish Q) */
{ "crh_f", KBD_TURKISH_F }, /* Crimean Tatar (Turkish F) */
{ "crh_alt", KBD_TURKISH_Q }, /* Crimean Tatar (Turkish Alt-Q) */
{ "", 0 },
};
/* Ukraine */
static const XKB_VARIANT ua_variants[] = {
{ "phonetic", KBD_UKRAINIAN }, /* Phonetic */
{ "typewriter", KBD_UKRAINIAN }, /* Typewriter */
{ "winkeys", KBD_UKRAINIAN }, /* Winkeys */
{ "legacy", KBD_UKRAINIAN }, /* Legacy */
{ "rstu", KBD_UKRAINIAN }, /* Standard RSTU */
{ "rstu_ru", KBD_UKRAINIAN }, /* Standard RSTU on Russian layout */
{ "homophonic", KBD_UKRAINIAN }, /* Homophonic */
{ "crh", KBD_TATAR }, /* Crimean Tatar (Turkish Q) */
{ "crh_f", KBD_TURKISH_F }, /* Crimean Tatar (Turkish F) */
{ "crh_alt", KBD_TURKISH_Q }, /* Crimean Tatar (Turkish Alt-Q) */
{ "", 0 },
};
/* United Kingdom */
static const XKB_VARIANT gb_variants[] = {
{ "extd", KBD_UNITED_KINGDOM_EXTENDED }, /* Extended - Winkeys */
{ "intl", KBD_UNITED_KINGDOM_EXTENDED }, /* International (with dead keys) */
{ "dvorak", KBD_UNITED_STATES_DVORAK }, /* Dvorak */
{ "dvorakukp", KBD_UNITED_STATES_DVORAK }, /* Dvorak (UK Punctuation) */
{ "mac", KBD_UNITED_KINGDOM }, /* Macintosh */
{ "colemak", 0 }, /* Colemak */
{ "", 0 },
};
/* Uzbekistan */
static const XKB_VARIANT uz_variants[] = {
{ "latin", 0 }, /* Latin */
{ "crh", KBD_TATAR }, /* Crimean Tatar (Turkish Q) */
{ "crh_f", KBD_TURKISH_F }, /* Crimean Tatar (Turkish F) */
{ "crh_alt", KBD_TURKISH_Q }, /* Crimean Tatar (Turkish Alt-Q) */
{ "", 0 },
};
/* Korea, Republic of */
static const XKB_VARIANT kr_variants[] = {
{ "kr104", KBD_KOREAN_INPUT_SYSTEM_IME_2000 }, /* 101/104 key Compatible */
{ "", 0 },
};
/* Ireland */
static const XKB_VARIANT ie_variants[] = {
{ "CloGaelach", KBD_GAELIC }, /* CloGaelach */
{ "UnicodeExpert", KBD_GAELIC }, /* UnicodeExpert */
{ "ogam", KBD_GAELIC }, /* Ogham */
{ "ogam_is434", KBD_GAELIC }, /* Ogham IS434 */
{ "", 0 },
};
/* Pakistan */
static const XKB_VARIANT pk_variants[] = {
{ "urd-crulp", 0 }, /* CRULP */
{ "urd-nla", 0 }, /* NLA */
{ "ara", KBD_ARABIC_101 }, /* Arabic */
{ "", 0 },
};
/* Esperanto */
static const XKB_VARIANT epo_variants[] = {
{ "legacy", 0 }, /* displaced semicolon and quote (obsolete) */
{ "", 0 },
};
/* Nigeria */
static const XKB_VARIANT ng_variants[] = {
{ "igbo", 0 }, /* Igbo */
{ "yoruba", 0 }, /* Yoruba */
{ "hausa", 0 }, /* Hausa */
{ "", 0 },
};
/* Braille */
static const XKB_VARIANT brai_variants[] = {
{ "left_hand", 0 }, /* Left hand */
{ "right_hand", 0 }, /* Right hand */
{ "", 0 },
};
/* Turkmenistan */
static const XKB_VARIANT tm_variants[] = {
{ "alt", KBD_TURKISH_Q }, /* Alt-Q */
{ "", 0 },
};
static const XKB_LAYOUT xkbLayouts[] = {
{ "us", KBD_US, us_variants }, /* USA */
{ "ad", 0, nullptr }, /* Andorra */
{ "af", KBD_FARSI, af_variants }, /* Afghanistan */
{ "ara", KBD_ARABIC_101, ara_variants }, /* Arabic */
{ "al", 0, nullptr }, /* Albania */
{ "am", KBD_ARMENIAN_EASTERN, am_variants }, /* Armenia */
{ "az", KBD_AZERI_CYRILLIC, az_variants }, /* Azerbaijan */
{ "by", KBD_BELARUSIAN, by_variants }, /* Belarus */
{ "be", KBD_BELGIAN_FRENCH, be_variants }, /* Belgium */
{ "bd", KBD_BENGALI, bd_variants }, /* Bangladesh */
{ "in", KBD_HINDI_TRADITIONAL, in_variants }, /* India */
{ "ba", KBD_CROATIAN, ba_variants }, /* Bosnia and Herzegovina */
{ "br", KBD_PORTUGUESE_BRAZILIAN_ABNT, br_variants }, /* Brazil */
{ "bg", KBD_BULGARIAN_LATIN, bg_variants }, /* Bulgaria */
{ "ma", KBD_FRENCH, ma_variants }, /* Morocco */
{ "mm", 0, nullptr }, /* Myanmar */
{ "ca", KBD_US, ca_variants }, /* Canada */
{ "cd", 0, nullptr }, /* Congo, Democratic Republic of the */
{ "cn", KBD_CHINESE_TRADITIONAL_PHONETIC, cn_variants }, /* China */
{ "hr", KBD_CROATIAN, hr_variants }, /* Croatia */
{ "cz", KBD_CZECH, cz_variants }, /* Czechia */
{ "dk", KBD_DANISH, dk_variants }, /* Denmark */
{ "nl", KBD_DUTCH, nl_variants }, /* Netherlands */
{ "bt", 0, nullptr }, /* Bhutan */
{ "ee", KBD_ESTONIAN, ee_variants }, /* Estonia */
{ "ir", 0, ir_variants }, /* Iran */
{ "iq", 0, iq_variants }, /* Iraq */
{ "fo", 0, fo_variants }, /* Faroe Islands */
{ "fi", KBD_FINNISH, fi_variants }, /* Finland */
{ "fr", KBD_FRENCH, fr_variants }, /* France */
{ "gh", 0, gh_variants }, /* Ghana */
{ "gn", 0, nullptr }, /* Guinea */
{ "ge", KBD_GEORGIAN, ge_variants }, /* Georgia */
{ "at", KBD_GERMAN, de_variants }, /* Austria */
{ "de", KBD_GERMAN, de_variants }, /* Germany */
{ "gr", KBD_GREEK, gr_variants }, /* Greece */
{ "hu", KBD_HUNGARIAN, hu_variants }, /* Hungary */
{ "is", KBD_ICELANDIC, is_variants }, /* Iceland */
{ "il", KBD_HEBREW, il_variants }, /* Israel */
{ "it", KBD_ITALIAN, it_variants }, /* Italy */
{ "jp", KBD_JAPANESE_INPUT_SYSTEM_MS_IME2002, jp_variants }, /* Japan */
{ "kg", 0, kg_variants }, /* Kyrgyzstan */
{ "kh", 0, nullptr }, /* Cambodia */
{ "kz", KBD_KAZAKH, kz_variants }, /* Kazakhstan */
{ "la", 0, nullptr }, /* Laos */
{ "latam", KBD_LATIN_AMERICAN, latam_variants }, /* Latin America */
{ "lt", KBD_LITHUANIAN, lt_variants }, /* Lithuania */
{ "lv", KBD_LATVIAN, lv_variants }, /* Latvia */
{ "mao", KBD_MAORI, nullptr }, /* Maori */
{ "me", KBD_SERBIAN_LATIN, me_variants }, /* Montenegro */
{ "mk", KBD_FYRO_MACEDONIAN, mk_variants }, /* Macedonia */
{ "mt", KBD_MALTESE_48_KEY, mt_variants }, /* Malta */
{ "mn", KBD_MONGOLIAN_CYRILLIC, nullptr }, /* Mongolia */
{ "no", KBD_NORWEGIAN, no_variants }, /* Norway */
{ "pl", KBD_POLISH_PROGRAMMERS, pl_variants }, /* Poland */
{ "pt", KBD_PORTUGUESE, pt_variants }, /* Portugal */
{ "ro", KBD_ROMANIAN, ro_variants }, /* Romania */
{ "ru", KBD_RUSSIAN, ru_variants }, /* Russia */
{ "rs", KBD_SERBIAN_LATIN, rs_variants }, /* Serbia */
{ "si", KBD_SLOVENIAN, si_variants }, /* Slovenia */
{ "sk", KBD_SLOVAK, sk_variants }, /* Slovakia */
{ "es", KBD_SPANISH, es_variants }, /* Spain */
{ "se", KBD_SWEDISH, se_variants }, /* Sweden */
{ "ch", KBD_SWISS_GERMAN, ch_variants }, /* Switzerland */
{ "sy", KBD_SYRIAC, sy_variants }, /* Syria */
{ "tj", 0, tj_variants }, /* Tajikistan */
{ "lk", 0, lk_variants }, /* Sri Lanka */
{ "th", KBD_THAI_KEDMANEE, th_variants }, /* Thailand */
{ "tr", KBD_TURKISH_Q, tr_variants }, /* Turkey */
{ "ua", KBD_UKRAINIAN, ua_variants }, /* Ukraine */
{ "gb", KBD_UNITED_KINGDOM, gb_variants }, /* United Kingdom */
{ "uz", KBD_UZBEK_CYRILLIC, uz_variants }, /* Uzbekistan */
{ "vn", KBD_VIETNAMESE, nullptr }, /* Vietnam */
{ "kr", KBD_KOREAN_INPUT_SYSTEM_IME_2000, kr_variants }, /* Korea, Republic of */
{ "ie", KBD_UNITED_KINGDOM, ie_variants }, /* Ireland */
{ "pk", 0, pk_variants }, /* Pakistan */
{ "mv", 0, nullptr }, /* Maldives */
{ "za", KBD_US, nullptr }, /* South Africa */
{ "epo", 0, epo_variants }, /* Esperanto */
{ "np", KBD_NEPALI, nullptr }, /* Nepal */
{ "ng", 0, ng_variants }, /* Nigeria */
{ "et", 0, nullptr }, /* Ethiopia */
{ "sn", 0, nullptr }, /* Senegal */
{ "brai", 0, brai_variants }, /* Braille */
{ "tm", KBD_TURKISH_Q, tm_variants }, /* Turkmenistan */
};
static uint32_t convert(int64_t val)
{
WINPR_ASSERT(val <= UINT32_MAX);
WINPR_ASSERT(val >= INT32_MIN);
return WINPR_CXX_COMPAT_CAST(uint32_t, val);
}
static UINT32 find_keyboard_layout_variant(const XKB_LAYOUT* layout, const char* variant)
{
WINPR_ASSERT(layout);
WINPR_ASSERT(variant);
const XKB_VARIANT* variants = layout->variants;
if (variants)
{
const XKB_VARIANT* var = variants;
while (var->variant && (strlen(var->variant) != 0))
{
if (strcmp(var->variant, variant) == 0)
return convert(var->keyboardLayoutID);
var++;
}
}
return convert(layout->keyboardLayoutID);
}
UINT32 find_keyboard_layout_in_xorg_rules(const char* layout, const char* variant)
{
if ((layout == nullptr) || (variant == nullptr))
return 0;
DEBUG_KBD("xkbLayout: %s\txkbVariant: %s", layout, variant);
for (size_t i = 0; i < ARRAYSIZE(xkbLayouts); i++)
{
const XKB_LAYOUT* cur = &xkbLayouts[i];
if (strcmp(cur->layout, layout) == 0)
return find_keyboard_layout_variant(cur, variant);
}
return 0;
}

View File

@@ -0,0 +1,31 @@
/**
* FreeRDP: A Remote Desktop Protocol Implementation
* RDP Keyboard layout ID detection from common X11 xkb keyboard layout names
*
* Copyright 2009-2012 Marc-Andre Moreau <marcandre.moreau@gmail.com>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef FREERDP_LIB_LOCALE_XKB_LAYOUT_IDS_H
#define FREERDP_LIB_LOCALE_XKB_LAYOUT_IDS_H
#include <freerdp/types.h>
#include <freerdp/api.h>
#if !defined(WITHOUT_FREERDP_3x_DEPRECATED)
WINPR_ATTR_NODISCARD
FREERDP_LOCAL UINT32 find_keyboard_layout_in_xorg_rules(const char* layout, const char* variant);
#endif
#endif /* FREERDP_LIB_LOCALE_XKB_LAYOUT_IDS_H */