From 8ea2c5dde623b05c3b227e9c6dc9299ea1e2f709 Mon Sep 17 00:00:00 2001 From: akallabeth Date: Mon, 3 Mar 2025 13:07:06 +0100 Subject: [PATCH 1/2] [client,x11] implement keyboard mapping * move to client code * fix parse_xkb_rule_names --- client/X11/CMakeLists.txt | 5 + client/X11/keyboard_x11.c | 144 +++++ client/X11/keyboard_x11.h | 24 + client/X11/xf_debug.h | 35 ++ client/X11/xf_event.c | 10 +- client/X11/xf_keyboard.c | 409 +++++++++++++- client/X11/xf_window.c | 10 +- client/X11/xfreerdp.h | 2 +- client/X11/xkb_layout_ids.c | 870 +++++++++++++++++++++++++++++ client/X11/xkb_layout_ids.h | 28 + libfreerdp/locale/keyboard_x11.c | 10 +- libfreerdp/locale/keyboard_x11.h | 4 +- libfreerdp/locale/xkb_layout_ids.h | 4 +- 13 files changed, 1526 insertions(+), 29 deletions(-) create mode 100644 client/X11/keyboard_x11.c create mode 100644 client/X11/keyboard_x11.h create mode 100644 client/X11/xf_debug.h create mode 100644 client/X11/xkb_layout_ids.c create mode 100644 client/X11/xkb_layout_ids.h diff --git a/client/X11/CMakeLists.txt b/client/X11/CMakeLists.txt index 85ee03868..3d521a928 100644 --- a/client/X11/CMakeLists.txt +++ b/client/X11/CMakeLists.txt @@ -48,6 +48,7 @@ set(SRCS xf_rail.h xf_input.c xf_input.h + xf_debug.h xf_event.c xf_event.h xf_floatbar.c @@ -66,6 +67,10 @@ set(SRCS xf_graphics.h xf_keyboard.c xf_keyboard.h + keyboard_x11.h + keyboard_x11.c + xkb_layout_ids.h + xkb_layout_ids.c xf_video.c xf_video.h xf_window.c diff --git a/client/X11/keyboard_x11.c b/client/X11/keyboard_x11.c new file mode 100644 index 000000000..73e11aa64 --- /dev/null +++ b/client/X11/keyboard_x11.c @@ -0,0 +1,144 @@ +/** + * FreeRDP: A Remote Desktop Protocol Implementation + * X11 Keyboard Mapping + * + * Copyright 2009-2012 Marc-Andre Moreau + * Copyright 2023 Bernhard Miklautz + * + * 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 + +#include +#include +#include + +#include "xf_debug.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 = NULL; + char* variant = NULL; + char* rule = NULL; + 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_X11("%s layout: %s, variant: %s", property_name, layout, variant); + layout_id = xf_find_keyboard_layout_in_xorg_rules(layout, variant); + + XFree(rule); + + return layout_id; +} + +int xf_detect_keyboard_layout_from_xkb(DWORD* keyboardLayoutId) +{ + Display* display = XOpenDisplay(NULL); + + 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; +} diff --git a/client/X11/keyboard_x11.h b/client/X11/keyboard_x11.h new file mode 100644 index 000000000..1666404d3 --- /dev/null +++ b/client/X11/keyboard_x11.h @@ -0,0 +1,24 @@ +/** + * FreeRDP: A Remote Desktop Protocol Implementation + * X11 Keyboard Mapping + * + * Copyright 2009-2012 Marc-Andre Moreau + * + * 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. + */ + +#pragma once + +#include + +int xf_detect_keyboard_layout_from_xkb(DWORD* keyboardLayoutId); diff --git a/client/X11/xf_debug.h b/client/X11/xf_debug.h new file mode 100644 index 000000000..9f5cba957 --- /dev/null +++ b/client/X11/xf_debug.h @@ -0,0 +1,35 @@ +/** + * FreeRDP: A Remote Desktop Protocol Implementation + * X11 debug helper header + * + * Copyright 2025 Armin Novak + * Copyright 2025 Thincast Technologies GmbH + * + * 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. + */ + +#pragma once + +#include +#include + +#define DBG_TAG CLIENT_TAG("x11") + +#ifdef WITH_DEBUG_X11 +#define DEBUG_X11(...) WLog_DBG(DBG_TAG, __VA_ARGS__) +#else +#define DEBUG_X11(...) \ + do \ + { \ + } while (0) +#endif diff --git a/client/X11/xf_event.c b/client/X11/xf_event.c index 5c6b08b06..25110b37a 100644 --- a/client/X11/xf_event.c +++ b/client/X11/xf_event.c @@ -41,6 +41,7 @@ #include "xf_graphics.h" #include "xf_utils.h" +#include "xf_debug.h" #include "xf_event.h" #define TAG CLIENT_TAG("x11") @@ -165,15 +166,6 @@ const char* x11_event_string(int event) } } -#ifdef WITH_DEBUG_X11 -#define DEBUG_X11(...) WLog_DBG(TAG, __VA_ARGS__) -#else -#define DEBUG_X11(...) \ - do \ - { \ - } while (0) -#endif - static BOOL xf_action_script_append(xfContext* xfc, const char* buffer, size_t size, WINPR_ATTR_UNUSED void* user, const char* what, const char* arg) { diff --git a/client/X11/xf_keyboard.c b/client/X11/xf_keyboard.c index 96b2341f0..6d0abc75e 100644 --- a/client/X11/xf_keyboard.c +++ b/client/X11/xf_keyboard.c @@ -39,12 +39,14 @@ #include #include +#include #include "xf_event.h" #include "xf_keyboard.h" #include "xf_utils.h" +#include "keyboard_x11.h" #include #define TAG CLIENT_TAG("x11") @@ -65,6 +67,263 @@ typedef struct BOOL RightSuper; } XF_MODIFIER_KEYS; +struct x11_key_scancode_t +{ + const char* name; + DWORD sc; +}; + +static const struct x11_key_scancode_t 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", RDP_SCANCODE_UNKNOWN }, /* 137: AGAI [Redo] */ + { "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 UINT32 xf_keyboard_get_toggle_keys_state(xfContext* xfc); static BOOL xf_keyboard_handle_special_keys(xfContext* xfc, KeySym keysym); static void xf_keyboard_handle_special_keys_release(xfContext* xfc, KeySym keysym); @@ -148,6 +407,107 @@ BOOL xf_keyboard_action_script_init(xfContext* xfc) return xf_event_action_script_init(xfc); } +static int xkb_cmp(const void* pva, const void* pvb) +{ + const struct x11_key_scancode_t* a = pva; + const struct x11_key_scancode_t* b = pvb; + + if (!a && !b) + return 0; + if (!a) + return 1; + if (!b) + return -1; + return strcmp(a->name, b->name); +} + +static BOOL try_add(xfContext* xfc, size_t offset, const char* xkb_keyname) +{ + WINPR_ASSERT(xfc); + + static BOOL initialized = FALSE; + static struct x11_key_scancode_t copy[ARRAYSIZE(XKB_KEY_NAME_SCANCODE_TABLE)] = { 0 }; + if (!initialized) + { + // TODO: Here we can do some magic: + // depending on input keyboard type use different mapping! (e.g. IBM, Apple, ...) + memcpy(copy, XKB_KEY_NAME_SCANCODE_TABLE, sizeof(copy)); + qsort(copy, ARRAYSIZE(copy), sizeof(struct x11_key_scancode_t), xkb_cmp); + initialized = TRUE; + } + + struct x11_key_scancode_t key = { .name = xkb_keyname, + .sc = WINPR_ASSERTING_INT_CAST(uint32_t, offset) }; + + struct x11_key_scancode_t* found = + bsearch(&key, copy, ARRAYSIZE(copy), sizeof(struct x11_key_scancode_t), xkb_cmp); + if (found) + { + WLog_Print(xfc->log, WLOG_DEBUG, + "%4s: keycode: 0x%02" PRIuz " -> rdp scancode: 0x%08" PRIx32 "", xkb_keyname, + offset, found->sc); + xfc->X11_KEYCODE_TO_VIRTUAL_SCANCODE[offset] = found->sc; + return TRUE; + } + + return FALSE; +} + +static int load_map_from_xkbfile(xfContext* xfc) +{ + WINPR_ASSERT(xfc); + int status = -1; + + if (!xfc->display) + return -2; + + XkbDescPtr xkb = XkbGetMap(xfc->display, 0, XkbUseCoreKbd); + if (!xkb) + { + WLog_Print(xfc->log, WLOG_WARN, "XkbGetMap() == NULL"); + return -3; + } + + const int rc = XkbGetNames(xfc->display, XkbKeyNamesMask, xkb); + if (rc != Success) + { + char buffer[64] = { 0 }; + WLog_Print(xfc->log, WLOG_WARN, "XkbGetNames() != Success: [%s]", + x11_error_to_string(xfc, rc, buffer, sizeof(buffer))); + } + else + { + char xkb_keyname[XkbKeyNameLength + 1] = { 42, 42, 42, 42, + 0 }; /* end-of-string at index 5 */ + + WLog_Print(xfc->log, WLOG_WARN, "XkbGetNames() == Success, min=%" PRIu8 ", max=%" PRIu8, + xkb->min_key_code, xkb->max_key_code); + for (size_t i = xkb->min_key_code; i < xkb->max_key_code; i++) + { + BOOL found = FALSE; + strncpy(xkb_keyname, xkb->names->keys[i].name, XkbKeyNameLength); + + WLog_Print(xfc->log, WLOG_WARN, "KeyCode %" PRIuz " -> %s", i, xkb_keyname); + if (strnlen(xkb_keyname, ARRAYSIZE(xkb_keyname)) < 1) + continue; + + found = try_add(xfc, i, xkb_keyname); + + if (!found) + { + WLog_Print(xfc->log, WLOG_WARN, "%4s: keycode: 0x%02X -> no RDP scancode found", + xkb_keyname, i); + } + else + status = 0; + } + } + + XkbFreeKeyboard(xkb, 0, 1); + + return status; +} + BOOL xf_keyboard_init(xfContext* xfc) { rdpSettings* settings = NULL; @@ -158,9 +518,28 @@ BOOL xf_keyboard_init(xfContext* xfc) WINPR_ASSERT(settings); xf_keyboard_clear(xfc); - xfc->KeyboardLayout = freerdp_settings_get_uint32(settings, FreeRDP_KeyboardLayout); - xfc->KeyboardLayout = freerdp_keyboard_init_ex(xfc->KeyboardLayout, NULL); - if (!freerdp_settings_set_uint32(settings, FreeRDP_KeyboardLayout, xfc->KeyboardLayout)) + + /* Layout detection algorithm: + * + * 1. If one was supplied, use that one + * 2. Try to determine current X11 keyboard layout + * 3. Try to determine default layout for current system language + * 4. Fall back to ENGLISH_UNITED_STATES + */ + UINT32 KeyboardLayout = freerdp_settings_get_uint32(settings, FreeRDP_KeyboardLayout); + if (KeyboardLayout == 0) + { + xf_detect_keyboard_layout_from_xkb(&KeyboardLayout); + if (KeyboardLayout == 0) + freerdp_detect_keyboard_layout_from_system_locale(&KeyboardLayout); + if (KeyboardLayout == 0) + KeyboardLayout = ENGLISH_UNITED_STATES; + if (!freerdp_settings_set_uint32(settings, FreeRDP_KeyboardLayout, KeyboardLayout)) + return FALSE; + } + + const int rc = load_map_from_xkbfile(xfc); + if (rc != 0) return FALSE; return xf_keyboard_update_modifier_map(xfc); @@ -201,6 +580,25 @@ void xf_keyboard_key_release(xfContext* xfc, const XKeyEvent* event, KeySym keys xf_keyboard_send_key(xfc, FALSE, last, event); } +static DWORD get_rdp_scancode_from_x11_keycode(xfContext* xfc, DWORD keycode) +{ + WINPR_ASSERT(xfc); + if (keycode >= ARRAYSIZE(xfc->X11_KEYCODE_TO_VIRTUAL_SCANCODE)) + { + WLog_ERR(TAG, "KeyCode %" PRIu32 " exceeds allowed value range [0,%" PRIuz "]", keycode, + ARRAYSIZE(xfc->X11_KEYCODE_TO_VIRTUAL_SCANCODE)); + return 0; + } + + const DWORD scancode = xfc->X11_KEYCODE_TO_VIRTUAL_SCANCODE[keycode]; + const DWORD remapped = freerdp_keyboard_remap_key(xfc->remap_table, scancode); + + if (remapped != 0) + return remapped; + + return scancode; +} + void xf_keyboard_release_all_keypress(xfContext* xfc) { WINPR_ASSERT(xfc); @@ -211,7 +609,7 @@ void xf_keyboard_release_all_keypress(xfContext* xfc) if (xfc->KeyboardState[keycode]) { const DWORD rdp_scancode = - freerdp_keyboard_get_rdp_scancode_from_x11_keycode((UINT32)keycode); + get_rdp_scancode_from_x11_keycode(xfc, WINPR_ASSERTING_INT_CAST(UINT32, keycode)); // release tab before releasing the windows key. // this stops the start menu from opening on unfocus event. @@ -242,8 +640,7 @@ void xf_keyboard_send_key(xfContext* xfc, BOOL down, BOOL repeat, const XKeyEven rdpInput* input = xfc->common.context.input; WINPR_ASSERT(input); - const DWORD sc = freerdp_keyboard_get_rdp_scancode_from_x11_keycode(event->keycode); - const DWORD rdp_scancode = freerdp_keyboard_remap_key(xfc->remap_table, sc); + const DWORD rdp_scancode = get_rdp_scancode_from_x11_keycode(xfc, event->keycode); if (rdp_scancode == RDP_SCANCODE_PAUSE && !xf_keyboard_key_pressed(xfc, XK_Control_L) && !xf_keyboard_key_pressed(xfc, XK_Control_R)) { diff --git a/client/X11/xf_window.c b/client/X11/xf_window.c index 34b4eb7df..a73ff8822 100644 --- a/client/X11/xf_window.c +++ b/client/X11/xf_window.c @@ -56,18 +56,10 @@ #include "xf_input.h" #include "xf_keyboard.h" #include "xf_utils.h" +#include "xf_debug.h" #define TAG CLIENT_TAG("x11") -#ifdef WITH_DEBUG_X11 -#define DEBUG_X11(...) WLog_DBG(TAG, __VA_ARGS__) -#else -#define DEBUG_X11(...) \ - do \ - { \ - } while (0) -#endif - #include #define xf_icon_prop FreeRDP_Icon_256px_prop diff --git a/client/X11/xfreerdp.h b/client/X11/xfreerdp.h index 639060a31..879023bf0 100644 --- a/client/X11/xfreerdp.h +++ b/client/X11/xfreerdp.h @@ -200,7 +200,6 @@ struct xf_context BOOL focused; BOOL mouse_active; BOOL fullscreen_toggle; - UINT32 KeyboardLayout; BOOL KeyboardState[256]; XModifierKeymap* modifierMap; wArrayList* keyCombinations; @@ -317,6 +316,7 @@ struct xf_context HANDLE pipethread; wLog* log; FREERDP_REMAP_TABLE* remap_table; + DWORD X11_KEYCODE_TO_VIRTUAL_SCANCODE[256]; }; BOOL xf_create_window(xfContext* xfc); diff --git a/client/X11/xkb_layout_ids.c b/client/X11/xkb_layout_ids.c new file mode 100644 index 000000000..b32033ee0 --- /dev/null +++ b/client/X11/xkb_layout_ids.c @@ -0,0 +1,870 @@ +/** + * FreeRDP: A Remote Desktop Protocol Implementation + * RDP Keyboard layout ID detection from common X11 xkb keyboard layout names + * + * Copyright 2009-2012 Marc-Andre Moreau + * + * 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 + +#include "xkb_layout_ids.h" + +#include + +#include + +#include "xf_debug.h" +#include + +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 <\|> 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, NULL }, /* Andorra */ + { "af", KBD_FARSI, af_variants }, /* Afghanistan */ + { "ara", KBD_ARABIC_101, ara_variants }, /* Arabic */ + { "al", 0, NULL }, /* 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, NULL }, /* Myanmar */ + { "ca", KBD_US, ca_variants }, /* Canada */ + { "cd", 0, NULL }, /* 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, NULL }, /* 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, NULL }, /* 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, NULL }, /* Cambodia */ + { "kz", KBD_KAZAKH, kz_variants }, /* Kazakhstan */ + { "la", 0, NULL }, /* Laos */ + { "latam", KBD_LATIN_AMERICAN, latam_variants }, /* Latin America */ + { "lt", KBD_LITHUANIAN, lt_variants }, /* Lithuania */ + { "lv", KBD_LATVIAN, lv_variants }, /* Latvia */ + { "mao", KBD_MAORI, NULL }, /* 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, NULL }, /* 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, NULL }, /* 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, NULL }, /* Maldives */ + { "za", KBD_US, NULL }, /* South Africa */ + { "epo", 0, epo_variants }, /* Esperanto */ + { "np", KBD_NEPALI, NULL }, /* Nepal */ + { "ng", 0, ng_variants }, /* Nigeria */ + { "et", 0, NULL }, /* Ethiopia */ + { "sn", 0, NULL }, /* 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 xf_find_keyboard_layout_in_xorg_rules(const char* layout, const char* variant) +{ + if ((layout == NULL) || (variant == NULL)) + return 0; + + DEBUG_X11("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; +} diff --git a/client/X11/xkb_layout_ids.h b/client/X11/xkb_layout_ids.h new file mode 100644 index 000000000..903e56aa3 --- /dev/null +++ b/client/X11/xkb_layout_ids.h @@ -0,0 +1,28 @@ +/** + * FreeRDP: A Remote Desktop Protocol Implementation + * RDP Keyboard layout ID detection from common X11 xkb keyboard layout names + * + * Copyright 2009-2012 Marc-Andre Moreau + * + * 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 +#include + +FREERDP_LOCAL UINT32 xf_find_keyboard_layout_in_xorg_rules(const char* layout, const char* variant); + +#endif /* FREERDP_LIB_LOCALE_XKB_LAYOUT_IDS_H */ diff --git a/libfreerdp/locale/keyboard_x11.c b/libfreerdp/locale/keyboard_x11.c index 471178082..c6b5d3200 100644 --- a/libfreerdp/locale/keyboard_x11.c +++ b/libfreerdp/locale/keyboard_x11.c @@ -49,6 +49,7 @@ static BOOL parse_xkb_rule_names(char* xkb_rule, unsigned long num_bytes, char** 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) { @@ -66,14 +67,19 @@ static BOOL parse_xkb_rule_names(char* xkb_rule, unsigned long num_bytes, char** 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; + } + break; case 4: // option break; default: break; } - i += strlen(ptr); } return TRUE; } diff --git a/libfreerdp/locale/keyboard_x11.h b/libfreerdp/locale/keyboard_x11.h index 0d6123d2f..563aca163 100644 --- a/libfreerdp/locale/keyboard_x11.h +++ b/libfreerdp/locale/keyboard_x11.h @@ -22,6 +22,8 @@ #include -FREERDP_LOCAL int freerdp_detect_keyboard_layout_from_xkb(DWORD* keyboardLayoutId); +WINPR_DEPRECATED_VAR( + "since 3.13.0, implement in client code", + FREERDP_LOCAL int freerdp_detect_keyboard_layout_from_xkb(DWORD* keyboardLayoutId)); #endif /* FREERDP_LOCALE_KEYBOARD_X11_H */ diff --git a/libfreerdp/locale/xkb_layout_ids.h b/libfreerdp/locale/xkb_layout_ids.h index e9c6b2757..2adb8fbee 100644 --- a/libfreerdp/locale/xkb_layout_ids.h +++ b/libfreerdp/locale/xkb_layout_ids.h @@ -23,6 +23,8 @@ #include #include -FREERDP_LOCAL UINT32 find_keyboard_layout_in_xorg_rules(const char* layout, const char* variant); +WINPR_DEPRECATED_VAR("since 3.13.0, implement in client code", + FREERDP_LOCAL UINT32 find_keyboard_layout_in_xorg_rules(const char* layout, + const char* variant)); #endif /* FREERDP_LIB_LOCALE_XKB_LAYOUT_IDS_H */ From d03b4b91d5219dd838696a3ada35531af9eb7981 Mon Sep 17 00:00:00 2001 From: akallabeth Date: Mon, 3 Mar 2025 17:26:41 +0100 Subject: [PATCH 2/2] [cmake,freerdp] add WITH_FREERDP_3x_DEPRECATED This new CMake option (ON by default) allows building the library with all symbols deprecated during 3.x release cycle disabled. This allows compatibility testing external applications for future FreeRDP 4.x support --- CMakeLists.txt | 5 +++++ include/CMakeLists.txt | 6 +++++- include/freerdp/codecs.h | 2 ++ include/freerdp/freerdp.h | 7 +++++++ include/freerdp/locale/keyboard.h | 2 ++ include/freerdp/server/proxy/proxy_config.h | 2 ++ include/freerdp/server/shadow.h | 4 ++++ libfreerdp/common/settings_getters.c | 4 ++++ libfreerdp/core/codecs.c | 2 ++ libfreerdp/core/freerdp.c | 6 ++++++ libfreerdp/locale/CMakeLists.txt | 10 ++++++---- libfreerdp/locale/keyboard.c | 12 ++++++++++-- libfreerdp/locale/keyboard_x11.h | 6 +++--- libfreerdp/locale/xkb_layout_ids.h | 6 +++--- server/shadow/shadow_capture.c | 2 ++ server/shadow/shadow_subsystem.c | 2 ++ tools/update-settings-tests | 4 ++++ winpr/include/winpr/custom-crypto.h | 2 ++ winpr/libwinpr/crypto/cipher.c | 2 ++ 19 files changed, 73 insertions(+), 13 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 81b80591d..bcd102511 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -163,6 +163,11 @@ if(WITH_FREERDP_DEPRECATED) add_compile_definitions(WITH_FREERDP_DEPRECATED) endif() +option(WITH_FREERDP_3x_DEPRECATED "Build FreeRDP 3x deprecated symbols" ON) +if(WITH_FREERDP_3x_DEPRECATED) + add_compile_definitions(WITH_FREERDP_3x_DEPRECATED) +endif() + option(WITH_FREERDP_DEPRECATED_COMMANDLINE "Build FreeRDP deprecated command line options" OFF) if(WITH_FREERDP_DEPRECATED_COMMANDLINE) add_compile_definitions(WITH_FREERDP_DEPRECATED_COMMANDLINE) diff --git a/include/CMakeLists.txt b/include/CMakeLists.txt index 7f9a7340a..17fd5e4bf 100644 --- a/include/CMakeLists.txt +++ b/include/CMakeLists.txt @@ -17,7 +17,11 @@ # See the License for the specific language governing permissions and # limitations under the License. -option(WITH_OPAQUE_SETTINGS "Hide rdpSettings struct definition, only allow getter/setter access" OFF) +if(WITH_FREERDP_3x_DEPRECATED) + option(WITH_OPAQUE_SETTINGS "Hide rdpSettings struct definition, only allow getter/setter access" OFF) +else() + set(WITH_OPAQUE_SETTINGS ON CACHE INTERNAL "WITH_FREERDP_3x_DEPRECATED") +endif() # prepare paths for C file(TO_NATIVE_PATH "${FREERDP_DATA_PATH}" NATIVE_FREERDP_DATA_PATH) diff --git a/include/freerdp/codecs.h b/include/freerdp/codecs.h index 8869b6109..e6b8c30ec 100644 --- a/include/freerdp/codecs.h +++ b/include/freerdp/codecs.h @@ -86,12 +86,14 @@ extern "C" WINPR_ATTR_MALLOC(freerdp_client_codecs_free, 1) FREERDP_API rdpCodecs* freerdp_client_codecs_new(UINT32 TheadingFlags); +#if defined(WITH_FREERDP_3x_DEPRECATED) WINPR_DEPRECATED_VAR("[since 3.6.0] Use freerdp_client_codecs_free", FREERDP_API void codecs_free(rdpCodecs* codecs)); WINPR_DEPRECATED_VAR("[since 3.6.0] Use freerdp_client_codecs_new", WINPR_ATTR_MALLOC(codecs_free, 1) FREERDP_API rdpCodecs* codecs_new(rdpContext* context)); +#endif #ifdef __cplusplus } diff --git a/include/freerdp/freerdp.h b/include/freerdp/freerdp.h index 91e60177b..7b3350b09 100644 --- a/include/freerdp/freerdp.h +++ b/include/freerdp/freerdp.h @@ -569,14 +569,18 @@ owned by rdpRdp */ FREERDP_API BOOL freerdp_connect(freerdp* instance); +#if defined(WITH_FREERDP_3x_DEPRECATED) WINPR_DEPRECATED_VAR("use freerdp_abort_connect_context instead", FREERDP_API BOOL freerdp_abort_connect(freerdp* instance)); +#endif FREERDP_API BOOL freerdp_abort_connect_context(rdpContext* context); FREERDP_API HANDLE freerdp_abort_event(rdpContext* context); +#if defined(WITH_FREERDP_3x_DEPRECATED) WINPR_DEPRECATED_VAR("use freerdp_shall_disconnect_context instead", FREERDP_API BOOL freerdp_shall_disconnect(freerdp* instance)); +#endif FREERDP_API BOOL freerdp_shall_disconnect_context(const rdpContext* context); FREERDP_API BOOL freerdp_disconnect(freerdp* instance); @@ -591,8 +595,11 @@ owned by rdpRdp */ */ FREERDP_API const char* freerdp_disconnect_reason_string(int reason); +#if defined(WITH_FREERDP_3x_DEPRECATED) WINPR_DEPRECATED_VAR("use freerdp_disconnect_before_reconnect_context instead", FREERDP_API BOOL freerdp_disconnect_before_reconnect(freerdp* instance)); +#endif + FREERDP_API BOOL freerdp_disconnect_before_reconnect_context(rdpContext* context); FREERDP_API BOOL freerdp_reconnect(freerdp* instance); diff --git a/include/freerdp/locale/keyboard.h b/include/freerdp/locale/keyboard.h index 82b13c3f4..4dcdf655a 100644 --- a/include/freerdp/locale/keyboard.h +++ b/include/freerdp/locale/keyboard.h @@ -262,6 +262,7 @@ FREERDP_API const char* freerdp_keyboard_get_layout_name_from_id(DWORD keyboardL */ FREERDP_API DWORD freerdp_keyboard_get_layout_id_from_name(const char* name); +#if defined(WITH_FREERDP_3x_DEPRECATED) WINPR_DEPRECATED_VAR("since 3.11.0, implement yourself in client", FREERDP_API DWORD freerdp_keyboard_init(DWORD keyboardLayoutId)); @@ -276,6 +277,7 @@ WINPR_DEPRECATED_VAR("since 3.11.0, implement yourself in client", WINPR_DEPRECATED_VAR("since 3.11.0, implement yourself in client", FREERDP_API DWORD freerdp_keyboard_get_x11_keycode_from_rdp_scancode( DWORD scancode, BOOL extended)); +#endif /** @brief deallocate a \b FREERDP_REMAP_TABLE * diff --git a/include/freerdp/server/proxy/proxy_config.h b/include/freerdp/server/proxy/proxy_config.h index 73f770706..98dd6dc45 100644 --- a/include/freerdp/server/proxy/proxy_config.h +++ b/include/freerdp/server/proxy/proxy_config.h @@ -86,11 +86,13 @@ extern "C" size_t InterceptCount; /* clipboard specific settings */ +#if defined(WITH_FREERDP_3x_DEPRECATED) WINPR_DEPRECATED_VAR("[since 3.6.0] Unused, ignore", BOOL TextOnly); WINPR_DEPRECATED_VAR("[since 3.6.0] Unused, ignore", UINT32 MaxTextLength); /* gfx settings */ WINPR_DEPRECATED_VAR("[since 3.6.0] Unused, ignore", BOOL DecodeGFX); +#endif /* modules */ char** Modules; /* module file names to load */ diff --git a/include/freerdp/server/shadow.h b/include/freerdp/server/shadow.h index 33cc0a369..bf36bc59e 100644 --- a/include/freerdp/server/shadow.h +++ b/include/freerdp/server/shadow.h @@ -303,11 +303,13 @@ extern "C" FREERDP_API void shadow_subsystem_set_entry_builtin(const char* name); FREERDP_API void shadow_subsystem_set_entry(pfnShadowSubsystemEntry pEntry); +#if defined(WITH_FREERDP_3x_DEPRECATED) WINPR_DEPRECATED_VAR( "[since 3.4.0] Use shadow_subsystem_pointer_convert_alpha_pointer_data_to_format instead", FREERDP_API int shadow_subsystem_pointer_convert_alpha_pointer_data( const BYTE* WINPR_RESTRICT pixels, BOOL premultiplied, UINT32 width, UINT32 height, SHADOW_MSG_OUT_POINTER_ALPHA_UPDATE* WINPR_RESTRICT pointerColor)); +#endif /** @brief Convert a pointer image from input format to RDP specific encoding * @@ -347,11 +349,13 @@ extern "C" FREERDP_API int shadow_capture_align_clip_rect(RECTANGLE_16* rect, const RECTANGLE_16* clip); +#if defined(WITH_FREERDP_3x_DEPRECATED) WINPR_DEPRECATED_VAR("[since 3.4.0] Use shadow_capture_compare_with_format", FREERDP_API int shadow_capture_compare( const BYTE* WINPR_RESTRICT pData1, UINT32 nStep1, UINT32 nWidth, UINT32 nHeight, const BYTE* WINPR_RESTRICT pData2, UINT32 nStep2, RECTANGLE_16* WINPR_RESTRICT rect)); +#endif /** @brief Compare two framebuffer images of possibly different formats with each other * diff --git a/libfreerdp/common/settings_getters.c b/libfreerdp/common/settings_getters.c index f41b8a9aa..6331933f3 100644 --- a/libfreerdp/common/settings_getters.c +++ b/libfreerdp/common/settings_getters.c @@ -2002,6 +2002,7 @@ UINT32 freerdp_settings_get_uint32(WINPR_ATTR_UNUSED const rdpSettings* settings case FreeRDP_VCFlags: return settings->VCFlags; +#if defined(WITH_FREERDP_3x_DEPRECATED) // API Compatibility section, remove with FreeRDP 4.x case (FreeRDP_Settings_Keys_UInt32)FreeRDP_MonitorLocalShiftX: return (UINT32)settings->MonitorLocalShiftX; @@ -2010,6 +2011,7 @@ UINT32 freerdp_settings_get_uint32(WINPR_ATTR_UNUSED const rdpSettings* settings case (FreeRDP_Settings_Keys_UInt32)FreeRDP_MonitorLocalShiftY: return (UINT32)settings->MonitorLocalShiftY; +#endif default: WLog_ERR(TAG, "Invalid key index %" PRIuz " [%s|%s]", id, freerdp_settings_get_name_for_key(id), @@ -2531,6 +2533,7 @@ BOOL freerdp_settings_set_uint32(WINPR_ATTR_UNUSED rdpSettings* settings, settings->VCFlags = cnv.c; break; +#if defined(WITH_FREERDP_3x_DEPRECATED) // API Compatibility section, remove with FreeRDP 4.x case FreeRDP_MonitorLocalShiftX: settings->MonitorLocalShiftX = (int32_t)cnv.c; @@ -2541,6 +2544,7 @@ BOOL freerdp_settings_set_uint32(WINPR_ATTR_UNUSED rdpSettings* settings, settings->MonitorLocalShiftY = (int32_t)cnv.c; break; +#endif default: WLog_ERR(TAG, "Invalid key index %" PRIuz " [%s|%s]", id, freerdp_settings_get_name_for_key(id), diff --git a/libfreerdp/core/codecs.c b/libfreerdp/core/codecs.c index 4dea7a47e..19c558aa3 100644 --- a/libfreerdp/core/codecs.c +++ b/libfreerdp/core/codecs.c @@ -239,6 +239,7 @@ BOOL freerdp_client_codecs_reset(rdpCodecs* codecs, UINT32 flags, UINT32 width, return rc; } +#if defined(WITH_FREERDP_3x_DEPRECATED) rdpCodecs* codecs_new(rdpContext* context) { if (!context || !context->settings) @@ -252,6 +253,7 @@ void codecs_free(rdpCodecs* codecs) { freerdp_client_codecs_free(codecs); } +#endif rdpCodecs* freerdp_client_codecs_new(UINT32 ThreadingFlags) { diff --git a/libfreerdp/core/freerdp.c b/libfreerdp/core/freerdp.c index 58852cf4f..e5388ec12 100644 --- a/libfreerdp/core/freerdp.c +++ b/libfreerdp/core/freerdp.c @@ -299,6 +299,7 @@ freerdp_connect_finally: return status; } +#if defined(WITH_FREERDP_3x_DEPRECATED) BOOL freerdp_abort_connect(freerdp* instance) { if (!instance) @@ -306,6 +307,7 @@ BOOL freerdp_abort_connect(freerdp* instance) return freerdp_abort_connect_context(instance->context); } +#endif BOOL freerdp_abort_connect_context(rdpContext* context) { @@ -636,11 +638,13 @@ BOOL freerdp_disconnect(freerdp* instance) return rc; } +#if defined(WITH_FREERDP_3x_DEPRECATED) BOOL freerdp_disconnect_before_reconnect(freerdp* instance) { WINPR_ASSERT(instance); return freerdp_disconnect_before_reconnect_context(instance->context); } +#endif BOOL freerdp_disconnect_before_reconnect_context(rdpContext* context) { @@ -669,6 +673,7 @@ BOOL freerdp_reconnect(freerdp* instance) return rdp_client_reconnect(rdp); } +#if defined(WITH_FREERDP_3x_DEPRECATED) BOOL freerdp_shall_disconnect(freerdp* instance) { if (!instance) @@ -676,6 +681,7 @@ BOOL freerdp_shall_disconnect(freerdp* instance) return freerdp_shall_disconnect_context(instance->context); } +#endif BOOL freerdp_shall_disconnect_context(const rdpContext* context) { diff --git a/libfreerdp/locale/CMakeLists.txt b/libfreerdp/locale/CMakeLists.txt index 322761c55..cead128b7 100644 --- a/libfreerdp/locale/CMakeLists.txt +++ b/libfreerdp/locale/CMakeLists.txt @@ -20,9 +20,11 @@ set(MODULE_PREFIX "FREERDP_LOCALE") set(SRCS keyboard_layout.c keyboard.c locale.c liblocale.h) -set(X11_SRCS keyboard_x11.c keyboard_x11.h xkb_layout_ids.c xkb_layout_ids.h) +if(WITH_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) + set(XKBFILE_SRCS keyboard_xkbfile.c keyboard_xkbfile.h) +endif() set(SUN_SRCS keyboard_sun.c keyboard_sun.h) @@ -52,7 +54,7 @@ if(APPLE) freerdp_library_add(${CORE_FOUNDATION}) endif() -if(WITH_X11) +if(WITH_X11 AND WITH_FREERDP_3x_DEPRECATED) find_package(X11 REQUIRED) freerdp_definition_add(WITH_X11) @@ -75,7 +77,7 @@ if(WITH_X11) endif() endif() -if(WITH_WAYLAND) +if(WITH_WAYLAND AND WITH_FREERDP_3x_DEPRECATED) freerdp_definition_add(WITH_WAYLAND) endif() diff --git a/libfreerdp/locale/keyboard.c b/libfreerdp/locale/keyboard.c index 34b3e9d40..f8744435e 100644 --- a/libfreerdp/locale/keyboard.c +++ b/libfreerdp/locale/keyboard.c @@ -35,25 +35,29 @@ #include "liblocale.h" +#if defined(WITH_FREERDP_3x_DEPRECATED) +#define TAG FREERDP_TAG("locale.keyboard") + #if defined(__MACOSX__) #include "keyboard_apple.h" #endif -#define TAG FREERDP_TAG("locale.keyboard") - #ifdef WITH_X11 #include "keyboard_x11.h" #ifdef WITH_XKBFILE #include "keyboard_xkbfile.h" #endif +#endif #endif +#if defined(WITH_FREERDP_3x_DEPRECATED) static WINPR_KEYCODE_TYPE maptype = WINPR_KEYCODE_TYPE_NONE; static DWORD VIRTUAL_SCANCODE_TO_X11_KEYCODE[256][2] = { 0 }; static DWORD X11_KEYCODE_TO_VIRTUAL_SCANCODE[256] = { 0 }; static DWORD REMAPPING_TABLE[0x10000] = { 0 }; +#endif struct rdp_remap_table { @@ -226,6 +230,7 @@ static const struct scancode_map_entry RDP_SCANCODE_MAP[] = { { RDP_SCANCODE_LAUNCH_APP2, "VK_LAUNCH_APP2" }, }; +#if defined(WITH_FREERDP_3x_DEPRECATED) static int freerdp_detect_keyboard(DWORD* keyboardLayoutId) { #if defined(_WIN32) @@ -348,6 +353,7 @@ DWORD freerdp_keyboard_init(DWORD keyboardLayoutId) return keyboardLayoutId; } +#endif FREERDP_REMAP_TABLE* freerdp_keyboard_remap_string_to_list(const char* list) { @@ -395,6 +401,7 @@ fail: return remap_table; } +#if defined(WITH_FREERDP_3x_DEPRECATED) DWORD freerdp_keyboard_init_ex(DWORD keyboardLayoutId, const char* keyboardRemappingList) { DWORD res = freerdp_keyboard_init(keyboardLayoutId); @@ -483,6 +490,7 @@ DWORD freerdp_keyboard_get_x11_keycode_from_rdp_scancode(DWORD scancode, BOOL ex else return x11[0]; } +#endif const char* freerdp_keyboard_scancode_name(DWORD scancode) { diff --git a/libfreerdp/locale/keyboard_x11.h b/libfreerdp/locale/keyboard_x11.h index 563aca163..72fe850ac 100644 --- a/libfreerdp/locale/keyboard_x11.h +++ b/libfreerdp/locale/keyboard_x11.h @@ -22,8 +22,8 @@ #include -WINPR_DEPRECATED_VAR( - "since 3.13.0, implement in client code", - FREERDP_LOCAL int freerdp_detect_keyboard_layout_from_xkb(DWORD* keyboardLayoutId)); +#if defined(WITH_FREERDP_3x_DEPRECATED) +FREERDP_LOCAL int freerdp_detect_keyboard_layout_from_xkb(DWORD* keyboardLayoutId); +#endif #endif /* FREERDP_LOCALE_KEYBOARD_X11_H */ diff --git a/libfreerdp/locale/xkb_layout_ids.h b/libfreerdp/locale/xkb_layout_ids.h index 2adb8fbee..ae8cce9f8 100644 --- a/libfreerdp/locale/xkb_layout_ids.h +++ b/libfreerdp/locale/xkb_layout_ids.h @@ -23,8 +23,8 @@ #include #include -WINPR_DEPRECATED_VAR("since 3.13.0, implement in client code", - FREERDP_LOCAL UINT32 find_keyboard_layout_in_xorg_rules(const char* layout, - const char* variant)); +#if defined(WITH_FREERDP_3x_DEPRECATED) +FREERDP_LOCAL UINT32 find_keyboard_layout_in_xorg_rules(const char* layout, const char* variant); +#endif #endif /* FREERDP_LIB_LOCALE_XKB_LAYOUT_IDS_H */ diff --git a/server/shadow/shadow_capture.c b/server/shadow/shadow_capture.c index bd1790ae6..b18839fab 100644 --- a/server/shadow/shadow_capture.c +++ b/server/shadow/shadow_capture.c @@ -76,6 +76,7 @@ int shadow_capture_align_clip_rect(RECTANGLE_16* rect, const RECTANGLE_16* clip) return 1; } +#if defined(WITH_FREERDP_3x_DEPRECATED) int shadow_capture_compare(const BYTE* WINPR_RESTRICT pData1, UINT32 nStep1, UINT32 nWidth, UINT32 nHeight, const BYTE* WINPR_RESTRICT pData2, UINT32 nStep2, RECTANGLE_16* WINPR_RESTRICT rect) @@ -83,6 +84,7 @@ int shadow_capture_compare(const BYTE* WINPR_RESTRICT pData1, UINT32 nStep1, UIN return shadow_capture_compare_with_format(pData1, PIXEL_FORMAT_BGRX32, nStep1, nWidth, nHeight, pData2, PIXEL_FORMAT_BGRX32, nStep2, rect); } +#endif static BOOL color_equal(UINT32 colorA, UINT32 formatA, UINT32 colorB, UINT32 formatB) { diff --git a/server/shadow/shadow_subsystem.c b/server/shadow/shadow_subsystem.c index bbdb568ef..c8ca4014e 100644 --- a/server/shadow/shadow_subsystem.c +++ b/server/shadow/shadow_subsystem.c @@ -189,6 +189,7 @@ UINT32 shadow_enum_monitors(MONITOR_DEF* monitors, UINT32 maxMonitors) * and andmask data and fill into SHADOW_MSG_OUT_POINTER_ALPHA_UPDATE * Caller should free the andMaskData and xorMaskData later. */ +#if defined(WITH_FREERDP_3x_DEPRECATED) int shadow_subsystem_pointer_convert_alpha_pointer_data( const BYTE* WINPR_RESTRICT pixels, BOOL premultiplied, UINT32 width, UINT32 height, SHADOW_MSG_OUT_POINTER_ALPHA_UPDATE* WINPR_RESTRICT pointerColor) @@ -196,6 +197,7 @@ int shadow_subsystem_pointer_convert_alpha_pointer_data( return shadow_subsystem_pointer_convert_alpha_pointer_data_to_format( pixels, PIXEL_FORMAT_BGRX32, premultiplied, width, height, pointerColor); } +#endif int shadow_subsystem_pointer_convert_alpha_pointer_data_to_format( const BYTE* pixels, UINT32 format, BOOL premultiplied, UINT32 width, UINT32 height, diff --git a/tools/update-settings-tests b/tools/update-settings-tests index 8d33ef613..66acc647b 100755 --- a/tools/update-settings-tests +++ b/tools/update-settings-tests @@ -101,11 +101,13 @@ def write_getter_body(f, values, ret, keys, isPointer, compat_values, typestr, e cast = '(void*)' write_getter_case(f, val, cast, None) if compat_values: + f.write('#if defined(WITH_FREERDP_3x_DEPRECATED)\n') for i in range(len(compat_values)): val = compat_values[i] cast = '(' + entry_type + ')' f.write('\t\t// API Compatibility section, remove with FreeRDP 4.x\n') write_getter_case(f, val, cast, typestr) + f.write('#endif\n') f.write('\t\tdefault:\n') f.write('\t\t\tWLog_ERR(TAG, "Invalid key index %" PRIuz " [%s|%s]", id, freerdp_settings_get_name_for_key(id), freerdp_settings_get_type_name_for_key(id));\n') f.write('\t\t\tWINPR_ASSERT(FALSE);\n') @@ -217,10 +219,12 @@ def write_setter(f, entry_dict, entry_type, entry_name, postfix, compat_dict): cast = '(' + k + ')' write_setter_case(f, val, postfix, isPointer, cast) if compat_values: + f.write('#if defined(WITH_FREERDP_3x_DEPRECATED)\n') for val in compat_values: cast = '(int32_t)' f.write('\t\t// API Compatibility section, remove with FreeRDP 4.x\n') write_setter_case(f, val, postfix, isPointer, cast) + f.write('#endif\n') f.write('\t\tdefault:\n') f.write('\t\t\tWLog_ERR(TAG, "Invalid key index %" PRIuz " [%s|%s]", id, freerdp_settings_get_name_for_key(id), freerdp_settings_get_type_name_for_key(id));\n') f.write('\t\t\treturn FALSE;\n') diff --git a/winpr/include/winpr/custom-crypto.h b/winpr/include/winpr/custom-crypto.h index e33a995cb..19dbc204b 100644 --- a/winpr/include/winpr/custom-crypto.h +++ b/winpr/include/winpr/custom-crypto.h @@ -264,12 +264,14 @@ extern "C" WINPR_API void winpr_Cipher_Free(WINPR_CIPHER_CTX* ctx); +#if defined(WITH_FREERDP_3x_DEPRECATED) WINPR_DEPRECATED_VAR("[since 3.10.0] use winpr_Cipher_NewEx", WINPR_ATTR_MALLOC(winpr_Cipher_Free, 1) WINPR_API WINPR_CIPHER_CTX* winpr_Cipher_New(WINPR_CIPHER_TYPE cipher, WINPR_CRYPTO_OPERATION op, const void* key, const void* iv)); +#endif /** @brief Create a new \b WINPR_CIPHER_CTX * diff --git a/winpr/libwinpr/crypto/cipher.c b/winpr/libwinpr/crypto/cipher.c index b9f64e065..0df944a5b 100644 --- a/winpr/libwinpr/crypto/cipher.c +++ b/winpr/libwinpr/crypto/cipher.c @@ -574,11 +574,13 @@ mbedtls_cipher_type_t winpr_mbedtls_get_cipher_type(int cipher) } #endif +#if defined(WITH_FREERDP_3x_DEPRECATED) WINPR_CIPHER_CTX* winpr_Cipher_New(WINPR_CIPHER_TYPE cipher, WINPR_CRYPTO_OPERATION op, const void* key, const void* iv) { return winpr_Cipher_NewEx(cipher, op, key, 0, iv, 0); } +#endif WINPR_API WINPR_CIPHER_CTX* winpr_Cipher_NewEx(WINPR_CIPHER_TYPE cipher, WINPR_CRYPTO_OPERATION op, const void* key, WINPR_ATTR_UNUSED size_t keylen,