FreeRDP
Loading...
Searching...
No Matches
xf_keyboard.c
1
20#include <freerdp/config.h>
21
22#include <ctype.h>
23#include <stdio.h>
24#include <stdlib.h>
25#include <string.h>
26#include <stdint.h>
27
28#include <winpr/crt.h>
29#include <winpr/path.h>
30#include <winpr/assert.h>
31#include <winpr/cast.h>
32#include <winpr/collections.h>
33
34#include <freerdp/utils/string.h>
35
36#include <X11/Xlib.h>
37#include <X11/Xutil.h>
38#include <X11/keysym.h>
39#include <X11/XKBlib.h>
40
41#include <freerdp/locale/keyboard.h>
42#include <freerdp/locale/locale.h>
43
44#include "xf_event.h"
45
46#include "xf_keyboard.h"
47
48#include "xf_utils.h"
49#include "keyboard_x11.h"
50
51#include <freerdp/log.h>
52#define TAG CLIENT_TAG("x11")
53
54struct x11_key_scancode_t
55{
56 const char* name;
57 DWORD sc;
58};
59
60static const struct x11_key_scancode_t XKB_KEY_NAME_SCANCODE_TABLE[] = {
61 { "AB01", RDP_SCANCODE_KEY_Z }, /* 052: AB01 [z] */
62 { "AB02", RDP_SCANCODE_KEY_X }, /* 053: AB02 [x] */
63 { "AB03", RDP_SCANCODE_KEY_C }, /* 054: AB03 [c] */
64 { "AB04", RDP_SCANCODE_KEY_V }, /* 055: AB04 [v] */
65 { "AB05", RDP_SCANCODE_KEY_B }, /* 056: AB05 [b] */
66 { "AB06", RDP_SCANCODE_KEY_N }, /* 057: AB06 [n] */
67 { "AB07", RDP_SCANCODE_KEY_M }, /* 058: AB07 [m] */
68 { "AB08", RDP_SCANCODE_OEM_COMMA }, /* 059: AB08 [comma] */
69 { "AB09", RDP_SCANCODE_OEM_PERIOD }, /* 060: AB09 [period] */
70 { "AB10", RDP_SCANCODE_OEM_2 }, /* 061: AB10 [slash] */
71 { "AB11", RDP_SCANCODE_ABNT_C1 }, /* 097: AB11 [(null)] */
72 { "AC01", RDP_SCANCODE_KEY_A }, /* 038: AC01 [a] */
73 { "AC02", RDP_SCANCODE_KEY_S }, /* 039: AC02 [s] */
74 { "AC03", RDP_SCANCODE_KEY_D }, /* 040: AC03 [d] */
75 { "AC04", RDP_SCANCODE_KEY_F }, /* 041: AC04 [f] */
76 { "AC05", RDP_SCANCODE_KEY_G }, /* 042: AC05 [g] */
77 { "AC06", RDP_SCANCODE_KEY_H }, /* 043: AC06 [h] */
78 { "AC07", RDP_SCANCODE_KEY_J }, /* 044: AC07 [j] */
79 { "AC08", RDP_SCANCODE_KEY_K }, /* 045: AC08 [k] */
80 { "AC09", RDP_SCANCODE_KEY_L }, /* 046: AC09 [l] */
81 { "AC10", RDP_SCANCODE_OEM_1 }, /* 047: AC10 [semicolon] */
82 { "AC11", RDP_SCANCODE_OEM_7 }, /* 048: AC11 [dead_acute] */
83 { "AD01", RDP_SCANCODE_KEY_Q }, /* 024: AD01 [q] */
84 { "AD02", RDP_SCANCODE_KEY_W }, /* 025: AD02 [w] */
85 { "AD03", RDP_SCANCODE_KEY_E }, /* 026: AD03 [e] */
86 { "AD04", RDP_SCANCODE_KEY_R }, /* 027: AD04 [r] */
87 { "AD05", RDP_SCANCODE_KEY_T }, /* 028: AD05 [t] */
88 { "AD06", RDP_SCANCODE_KEY_Y }, /* 029: AD06 [y] */
89 { "AD07", RDP_SCANCODE_KEY_U }, /* 030: AD07 [u] */
90 { "AD08", RDP_SCANCODE_KEY_I }, /* 031: AD08 [i] */
91 { "AD09", RDP_SCANCODE_KEY_O }, /* 032: AD09 [o] */
92 { "AD10", RDP_SCANCODE_KEY_P }, /* 033: AD10 [p] */
93 { "AD11", RDP_SCANCODE_OEM_4 }, /* 034: AD11 [bracketleft] */
94 { "AD12", RDP_SCANCODE_OEM_6 }, /* 035: AD12 [bracketright] */
95 { "AE01", RDP_SCANCODE_KEY_1 }, /* 010: AE01 [1] */
96 { "AE02", RDP_SCANCODE_KEY_2 }, /* 011: AE02 [2] */
97 { "AE03", RDP_SCANCODE_KEY_3 }, /* 012: AE03 [3] */
98 { "AE04", RDP_SCANCODE_KEY_4 }, /* 013: AE04 [4] */
99 { "AE05", RDP_SCANCODE_KEY_5 }, /* 014: AE05 [5] */
100 { "AE06", RDP_SCANCODE_KEY_6 }, /* 015: AE06 [6] */
101 { "AE07", RDP_SCANCODE_KEY_7 }, /* 016: AE07 [7] */
102 { "AE08", RDP_SCANCODE_KEY_8 }, /* 017: AE08 [8] */
103 { "AE09", RDP_SCANCODE_KEY_9 }, /* 018: AE09 [9] */
104 { "AE10", RDP_SCANCODE_KEY_0 }, /* 019: AE10 [0] */
105 { "AE11", RDP_SCANCODE_OEM_MINUS }, /* 020: AE11 [minus] */
106 { "AE12", RDP_SCANCODE_OEM_PLUS }, /* 021: AE12 [equal] */
107 { "AE13", RDP_SCANCODE_BACKSLASH_JP }, /* 132: AE13 [(null)] */
108 { "AGAI" /* codespell:ignore */, RDP_SCANCODE_UNKNOWN },
109 { "ALT", RDP_SCANCODE_LMENU }, /* 204: ALT [(null)] */
110 { "BKSL", RDP_SCANCODE_OEM_5 }, /* 051: BKSL [backslash] */
111 { "BKSP", RDP_SCANCODE_BACKSPACE }, /* 022: BKSP [BackSpace] */
112 { "CAPS", RDP_SCANCODE_CAPSLOCK }, /* 066: CAPS [Caps_Lock] */
113 { "COMP", RDP_SCANCODE_APPS }, /* 135: COMP [Menu] */
114 { "COPY", RDP_SCANCODE_UNKNOWN }, /* 141: COPY [XF86Copy] */
115 { "CUT", RDP_SCANCODE_UNKNOWN }, /* 145: CUT [XF86Cut] */
116 { "DELE", RDP_SCANCODE_DELETE }, /* 119: DELE [Delete] */
117 { "DOWN", RDP_SCANCODE_DOWN }, /* 116: DOWN [Down] */
118 { "END", RDP_SCANCODE_END }, /* 115: END [End] */
119 { "ESC", RDP_SCANCODE_ESCAPE }, /* 009: ESC [Escape] */
120 { "FIND", RDP_SCANCODE_UNKNOWN }, /* 144: FIND [Find] */
121 { "FK01", RDP_SCANCODE_F1 }, /* 067: FK01 [F1] */
122 { "FK02", RDP_SCANCODE_F2 }, /* 068: FK02 [F2] */
123 { "FK03", RDP_SCANCODE_F3 }, /* 069: FK03 [F3] */
124 { "FK04", RDP_SCANCODE_F4 }, /* 070: FK04 [F4] */
125 { "FK05", RDP_SCANCODE_F5 }, /* 071: FK05 [F5] */
126 { "FK06", RDP_SCANCODE_F6 }, /* 072: FK06 [F6] */
127 { "FK07", RDP_SCANCODE_F7 }, /* 073: FK07 [F7] */
128 { "FK08", RDP_SCANCODE_F8 }, /* 074: FK08 [F8] */
129 { "FK09", RDP_SCANCODE_F9 }, /* 075: FK09 [F9] */
130 { "FK10", RDP_SCANCODE_F10 }, /* 076: FK10 [F10] */
131 { "FK11", RDP_SCANCODE_F11 }, /* 095: FK11 [F11] */
132 { "FK12", RDP_SCANCODE_F12 }, /* 096: FK12 [F12] */
133 { "FK13", RDP_SCANCODE_F13 }, /* 191: FK13 [XF86Tools] */
134 { "FK14", RDP_SCANCODE_F14 }, /* 192: FK14 [XF86Launch5] */
135 { "FK15", RDP_SCANCODE_F15 }, /* 193: FK15 [XF86Launch6] */
136 { "FK16", RDP_SCANCODE_F16 }, /* 194: FK16 [XF86Launch7] */
137 { "FK17", RDP_SCANCODE_F17 }, /* 195: FK17 [XF86Launch8] */
138 { "FK18", RDP_SCANCODE_F18 }, /* 196: FK18 [XF86Launch9] */
139 { "FK19", RDP_SCANCODE_F19 }, /* 197: FK19 [(null)] */
140 { "FK20", RDP_SCANCODE_F20 }, /* 198: FK20 [XF86AudioMicMute] */
141 { "FK21", RDP_SCANCODE_F21 }, /* 199: FK21 [XF86TouchpadToggle] */
142 { "FK22", RDP_SCANCODE_F22 }, /* 200: FK22 [XF86TouchpadOn] */
143 { "FK23", RDP_SCANCODE_F23 }, /* 201: FK23 [XF86TouchpadOff] */
144 { "FK24", RDP_SCANCODE_F24 }, /* 202: FK24 [(null)] */
145 { "FRNT", RDP_SCANCODE_UNKNOWN }, /* 140: FRNT [SunFront] */
146 { "HELP", RDP_SCANCODE_HELP }, /* 146: HELP [Help] */
147 { "HENK", RDP_SCANCODE_CONVERT_JP }, /* 100: HENK [Henkan_Mode] */
148 { "HIRA", RDP_SCANCODE_HIRAGANA }, /* 099: HIRA [Hiragana] */
149 { "HJCV", RDP_SCANCODE_HANJA }, /* 131: HJCV [Hangul_Hanja] */
150 { "HKTG", RDP_SCANCODE_HIRAGANA }, /* 101: HKTG [Hiragana_Katakana] */
151 { "HNGL", RDP_SCANCODE_HANGUL }, /* 130: HNGL [Hangul] */
152 { "HOME", RDP_SCANCODE_HOME }, /* 110: HOME [Home] */
153 { "HYPR", RDP_SCANCODE_LWIN }, /* 207: HYPR [(null)] */
154 { "I120", RDP_SCANCODE_UNKNOWN }, /* 120: I120 [(null)] */
155 { "I126", RDP_SCANCODE_UNKNOWN }, /* 126: I126 [plusminus] */
156 { "I128", RDP_SCANCODE_LAUNCH_MEDIA_SELECT }, /* 128: I128 [XF86LaunchA] */
157 { "I129", RDP_SCANCODE_ABNT_C2 }, /* 129: I129 [KP_Decimal] */
158 { "I147", RDP_SCANCODE_UNKNOWN }, /* 147: I147 [XF86MenuKB] */
159 { "I148", RDP_SCANCODE_UNKNOWN }, /* 148: I148 [XF86Calculator] */
160 { "I149", RDP_SCANCODE_UNKNOWN }, /* 149: I149 [(null)] */
161 { "I150", RDP_SCANCODE_SLEEP }, /* 150: I150 [XF86Sleep] */
162 { "I151", RDP_SCANCODE_UNKNOWN }, /* 151: I151 [XF86WakeUp] */
163 { "I152", RDP_SCANCODE_UNKNOWN }, /* 152: I152 [XF86Explorer] */
164 { "I153", RDP_SCANCODE_UNKNOWN }, /* 153: I153 [XF86Send] */
165 { "I154", RDP_SCANCODE_UNKNOWN }, /* 154: I154 [(null)] */
166 { "I155", RDP_SCANCODE_UNKNOWN }, /* 155: I155 [XF86Xfer] */
167 { "I156", RDP_SCANCODE_LAUNCH_APP1 }, /* 156: I156 [XF86Launch1] */
168 { "I157", RDP_SCANCODE_LAUNCH_APP2 }, /* 157: I157 [XF86Launch2] */
169 { "I158", RDP_SCANCODE_BROWSER_HOME }, /* 158: I158 [XF86WWW] */
170 { "I159", RDP_SCANCODE_UNKNOWN }, /* 159: I159 [XF86DOS] */
171 { "I160", RDP_SCANCODE_UNKNOWN }, /* 160: I160 [XF86ScreenSaver] */
172 { "I161", RDP_SCANCODE_UNKNOWN }, /* 161: I161 [XF86RotateWindows] */
173 { "I162", RDP_SCANCODE_UNKNOWN }, /* 162: I162 [XF86TaskPane] */
174 { "I163", RDP_SCANCODE_LAUNCH_MAIL }, /* 163: I163 [XF86Mail] */
175 { "I164", RDP_SCANCODE_BROWSER_FAVORITES }, /* 164: I164 [XF86Favorites] */
176 { "I165", RDP_SCANCODE_UNKNOWN }, /* 165: I165 [XF86MyComputer] */
177 { "I166", RDP_SCANCODE_BROWSER_BACK }, /* 166: I166 [XF86Back] */
178 { "I167", RDP_SCANCODE_BROWSER_FORWARD }, /* 167: I167 [XF86Forward] */
179 { "I168", RDP_SCANCODE_UNKNOWN }, /* 168: I168 [(null)] */
180 { "I169", RDP_SCANCODE_UNKNOWN }, /* 169: I169 [XF86Eject] */
181 { "I170", RDP_SCANCODE_UNKNOWN }, /* 170: I170 [XF86Eject] */
182 { "I171", RDP_SCANCODE_MEDIA_NEXT_TRACK }, /* 171: I171 [XF86AudioNext] */
183 { "I172", RDP_SCANCODE_MEDIA_PLAY_PAUSE }, /* 172: I172 [XF86AudioPlay] */
184 { "I173", RDP_SCANCODE_MEDIA_PREV_TRACK }, /* 173: I173 [XF86AudioPrev] */
185 { "I174", RDP_SCANCODE_MEDIA_STOP }, /* 174: I174 [XF86AudioStop] */
186 { "I175", RDP_SCANCODE_UNKNOWN }, /* 175: I175 [XF86AudioRecord] */
187 { "I176", RDP_SCANCODE_UNKNOWN }, /* 176: I176 [XF86AudioRewind] */
188 { "I177", RDP_SCANCODE_UNKNOWN }, /* 177: I177 [XF86Phone] */
189 { "I178", RDP_SCANCODE_UNKNOWN }, /* 178: I178 [(null)] */
190 { "I179", RDP_SCANCODE_UNKNOWN }, /* 179: I179 [XF86Tools] */
191 { "I180", RDP_SCANCODE_BROWSER_HOME }, /* 180: I180 [XF86HomePage] */
192 { "I181", RDP_SCANCODE_BROWSER_REFRESH }, /* 181: I181 [XF86Reload] */
193 { "I182", RDP_SCANCODE_UNKNOWN }, /* 182: I182 [XF86Close] */
194 { "I183", RDP_SCANCODE_UNKNOWN }, /* 183: I183 [(null)] */
195 { "I184", RDP_SCANCODE_UNKNOWN }, /* 184: I184 [(null)] */
196 { "I185", RDP_SCANCODE_UNKNOWN }, /* 185: I185 [XF86ScrollUp] */
197 { "I186", RDP_SCANCODE_UNKNOWN }, /* 186: I186 [XF86ScrollDown] */
198 { "I187", RDP_SCANCODE_UNKNOWN }, /* 187: I187 [parenleft] */
199 { "I188", RDP_SCANCODE_UNKNOWN }, /* 188: I188 [parenright] */
200 { "I189", RDP_SCANCODE_UNKNOWN }, /* 189: I189 [XF86New] */
201 { "I190", RDP_SCANCODE_UNKNOWN }, /* 190: I190 [Redo] */
202 { "I208", RDP_SCANCODE_MEDIA_PLAY_PAUSE }, /* 208: I208 [XF86AudioPlay] */
203 { "I209", RDP_SCANCODE_MEDIA_PLAY_PAUSE }, /* 209: I209 [XF86AudioPause] */
204 { "I210", RDP_SCANCODE_UNKNOWN }, /* 210: I210 [XF86Launch3] */
205 { "I211", RDP_SCANCODE_UNKNOWN }, /* 211: I211 [XF86Launch4] */
206 { "I212", RDP_SCANCODE_UNKNOWN }, /* 212: I212 [XF86LaunchB] */
207 { "I213", RDP_SCANCODE_UNKNOWN }, /* 213: I213 [XF86Suspend] */
208 { "I214", RDP_SCANCODE_UNKNOWN }, /* 214: I214 [XF86Close] */
209 { "I215", RDP_SCANCODE_MEDIA_PLAY_PAUSE }, /* 215: I215 [XF86AudioPlay] */
210 { "I216", RDP_SCANCODE_MEDIA_NEXT_TRACK }, /* 216: I216 [XF86AudioForward] */
211 { "I217", RDP_SCANCODE_UNKNOWN }, /* 217: I217 [(null)] */
212 { "I218", RDP_SCANCODE_UNKNOWN }, /* 218: I218 [Print] */
213 { "I219", RDP_SCANCODE_UNKNOWN }, /* 219: I219 [(null)] */
214 { "I220", RDP_SCANCODE_UNKNOWN }, /* 220: I220 [XF86WebCam] */
215 { "I221", RDP_SCANCODE_UNKNOWN }, /* 221: I221 [XF86AudioPreset] */
216 { "I222", RDP_SCANCODE_UNKNOWN }, /* 222: I222 [(null)] */
217 { "I223", RDP_SCANCODE_LAUNCH_MAIL }, /* 223: I223 [XF86Mail] */
218 { "I224", RDP_SCANCODE_UNKNOWN }, /* 224: I224 [XF86Messenger] */
219 { "I225", RDP_SCANCODE_BROWSER_SEARCH }, /* 225: I225 [XF86Search] */
220 { "I226", RDP_SCANCODE_UNKNOWN }, /* 226: I226 [XF86Go] */
221 { "I227", RDP_SCANCODE_UNKNOWN }, /* 227: I227 [XF86Finance] */
222 { "I228", RDP_SCANCODE_UNKNOWN }, /* 228: I228 [XF86Game] */
223 { "I229", RDP_SCANCODE_UNKNOWN }, /* 229: I229 [XF86Shop] */
224 { "I230", RDP_SCANCODE_UNKNOWN }, /* 230: I230 [(null)] */
225 { "I231", RDP_SCANCODE_UNKNOWN }, /* 231: I231 [Cancel] */
226 { "I232", RDP_SCANCODE_UNKNOWN }, /* 232: I232 [XF86MonBrightnessDown] */
227 { "I233", RDP_SCANCODE_UNKNOWN }, /* 233: I233 [XF86MonBrightnessUp] */
228 { "I234", RDP_SCANCODE_LAUNCH_MEDIA_SELECT }, /* 234: I234 [XF86AudioMedia] */
229 { "I235", RDP_SCANCODE_UNKNOWN }, /* 235: I235 [XF86Display] */
230 { "I236", RDP_SCANCODE_UNKNOWN }, /* 236: I236 [XF86KbdLightOnOff] */
231 { "I237", RDP_SCANCODE_UNKNOWN }, /* 237: I237 [XF86KbdBrightnessDown] */
232 { "I238", RDP_SCANCODE_UNKNOWN }, /* 238: I238 [XF86KbdBrightnessUp] */
233 { "I239", RDP_SCANCODE_UNKNOWN }, /* 239: I239 [XF86Send] */
234 { "I240", RDP_SCANCODE_UNKNOWN }, /* 240: I240 [XF86Reply] */
235 { "I241", RDP_SCANCODE_UNKNOWN }, /* 241: I241 [XF86MailForward] */
236 { "I242", RDP_SCANCODE_UNKNOWN }, /* 242: I242 [XF86Save] */
237 { "I243", RDP_SCANCODE_UNKNOWN }, /* 243: I243 [XF86Documents] */
238 { "I244", RDP_SCANCODE_UNKNOWN }, /* 244: I244 [XF86Battery] */
239 { "I245", RDP_SCANCODE_UNKNOWN }, /* 245: I245 [XF86Bluetooth] */
240 { "I246", RDP_SCANCODE_UNKNOWN }, /* 246: I246 [XF86WLAN] */
241 { "I247", RDP_SCANCODE_UNKNOWN }, /* 247: I247 [XF86UWB] */
242 { "I248", RDP_SCANCODE_UNKNOWN }, /* 248: I248 [(null)] */
243 { "I249", RDP_SCANCODE_UNKNOWN }, /* 249: I249 [XF86Next_VMode] */
244 { "I250", RDP_SCANCODE_UNKNOWN }, /* 250: I250 [XF86Prev_VMode] */
245 { "I251", RDP_SCANCODE_UNKNOWN }, /* 251: I251 [XF86MonBrightnessCycle] */
246 { "I252", RDP_SCANCODE_UNKNOWN }, /* 252: I252 [XF86BrightnessAuto] */
247 { "I253", RDP_SCANCODE_UNKNOWN }, /* 253: I253 [XF86DisplayOff] */
248 { "I254", RDP_SCANCODE_UNKNOWN }, /* 254: I254 [XF86WWAN] */
249 { "I255", RDP_SCANCODE_UNKNOWN }, /* 255: I255 [XF86RFKill] */
250 { "INS", RDP_SCANCODE_INSERT }, /* 118: INS [Insert] */
251 { "JPCM", RDP_SCANCODE_UNKNOWN }, /* 103: JPCM [(null)] */
252 { "KATA", RDP_SCANCODE_KANA_HANGUL }, /* 098: KATA [Katakana] */
253 { "KP0", RDP_SCANCODE_NUMPAD0 }, /* 090: KP0 [KP_Insert] */
254 { "KP1", RDP_SCANCODE_NUMPAD1 }, /* 087: KP1 [KP_End] */
255 { "KP2", RDP_SCANCODE_NUMPAD2 }, /* 088: KP2 [KP_Down] */
256 { "KP3", RDP_SCANCODE_NUMPAD3 }, /* 089: KP3 [KP_Next] */
257 { "KP4", RDP_SCANCODE_NUMPAD4 }, /* 083: KP4 [KP_Left] */
258 { "KP5", RDP_SCANCODE_NUMPAD5 }, /* 084: KP5 [KP_Begin] */
259 { "KP6", RDP_SCANCODE_NUMPAD6 }, /* 085: KP6 [KP_Right] */
260 { "KP7", RDP_SCANCODE_NUMPAD7 }, /* 079: KP7 [KP_Home] */
261 { "KP8", RDP_SCANCODE_NUMPAD8 }, /* 080: KP8 [KP_Up] */
262 { "KP9", RDP_SCANCODE_NUMPAD9 }, /* 081: KP9 [KP_Prior] */
263 { "KPAD", RDP_SCANCODE_ADD }, /* 086: KPAD [KP_Add] */
264 { "KPDL", RDP_SCANCODE_DECIMAL }, /* 091: KPDL [KP_Delete] */
265 { "KPDV", RDP_SCANCODE_DIVIDE }, /* 106: KPDV [KP_Divide] */
266 { "KPEN", RDP_SCANCODE_RETURN_KP }, /* 104: KPEN [KP_Enter] */
267 { "KPEQ", RDP_SCANCODE_UNKNOWN }, /* 125: KPEQ [KP_Equal] */
268 { "KPMU", RDP_SCANCODE_MULTIPLY }, /* 063: KPMU [KP_Multiply] */
269 { "KPSU", RDP_SCANCODE_SUBTRACT }, /* 082: KPSU [KP_Subtract] */
270 { "LALT", RDP_SCANCODE_LMENU }, /* 064: LALT [Alt_L] */
271 { "LCTL", RDP_SCANCODE_LCONTROL }, /* 037: LCTL [Control_L] */
272 { "LEFT", RDP_SCANCODE_LEFT }, /* 113: LEFT [Left] */
273 { "LFSH", RDP_SCANCODE_LSHIFT }, /* 050: LFSH [Shift_L] */
274 { "LNFD", RDP_SCANCODE_UNKNOWN }, /* 109: LNFD [Linefeed] */
275 { "LSGT", RDP_SCANCODE_OEM_102 }, /* 094: LSGT [backslash] */
276 { "LVL3", RDP_SCANCODE_RMENU }, /* 092: LVL3 [ISO_Level3_Shift] */
277 { "LVL5", RDP_SCANCODE_UNKNOWN }, /* 203: LVL5 [ISO_Level5_Shift] */
278 { "LWIN", RDP_SCANCODE_LWIN }, /* 133: LWIN [Super_L] */
279 { "META", RDP_SCANCODE_LMENU }, /* 205: META [(null)] */
280 { "MUHE", RDP_SCANCODE_NONCONVERT_JP }, /* 102: MUHE [Muhenkan] */
281 { "MUTE", RDP_SCANCODE_VOLUME_MUTE }, /* 121: MUTE [XF86AudioMute] */
282 { "NMLK", RDP_SCANCODE_NUMLOCK }, /* 077: NMLK [Num_Lock] */
283 { "OPEN", RDP_SCANCODE_UNKNOWN }, /* 142: OPEN [XF86Open] */
284 { "PAST", RDP_SCANCODE_UNKNOWN }, /* 143: PAST [XF86Paste] */
285 { "PAUS", RDP_SCANCODE_PAUSE }, /* 127: PAUS [Pause] */
286 { "PGDN", RDP_SCANCODE_NEXT }, /* 117: PGDN [Next] */
287 { "PGUP", RDP_SCANCODE_PRIOR }, /* 112: PGUP [Prior] */
288 { "POWR", RDP_SCANCODE_UNKNOWN }, /* 124: POWR [XF86PowerOff] */
289 { "PROP", RDP_SCANCODE_UNKNOWN }, /* 138: PROP [SunProps] */
290 { "PRSC", RDP_SCANCODE_PRINTSCREEN }, /* 107: PRSC [Print] */
291 { "RALT", RDP_SCANCODE_RMENU }, /* 108: RALT [ISO_Level3_Shift] */
292 { "RCTL", RDP_SCANCODE_RCONTROL }, /* 105: RCTL [Control_R] */
293 { "RGHT", RDP_SCANCODE_RIGHT }, /* 114: RGHT [Right] */
294 { "RTRN", RDP_SCANCODE_RETURN }, /* 036: RTRN [Return] */
295 { "RTSH", RDP_SCANCODE_RSHIFT }, /* 062: RTSH [Shift_R] */
296 { "RWIN", RDP_SCANCODE_RWIN }, /* 134: RWIN [Super_R] */
297 { "SCLK", RDP_SCANCODE_SCROLLLOCK }, /* 078: SCLK [Multi_key] */
298 { "SPCE", RDP_SCANCODE_SPACE }, /* 065: SPCE [space] */
299 { "STOP", RDP_SCANCODE_BROWSER_STOP }, /* 136: STOP [Cancel] */
300 { "SUPR", RDP_SCANCODE_LWIN }, /* 206: SUPR [(null)] */
301 { "TAB", RDP_SCANCODE_TAB }, /* 023: TAB [Tab] */
302 { "TLDE", RDP_SCANCODE_OEM_3 }, /* 049: TLDE [dead_grave] */
303 { "UNDO", RDP_SCANCODE_UNKNOWN }, /* 139: UNDO [Undo] */
304 { "UP", RDP_SCANCODE_UP }, /* 111: UP [Up] */
305 { "VOL+", RDP_SCANCODE_VOLUME_UP }, /* 123: VOL+ [XF86AudioRaiseVolume] */
306 { "VOL-", RDP_SCANCODE_VOLUME_DOWN } /* 122: VOL- [XF86AudioLowerVolume] */
307};
308
309typedef struct
310{
311 KeySym keysym;
312 DWORD sc;
313} x11_keysym_scancode_t;
314
315static const x11_keysym_scancode_t KEYSYM_SCANCODE_TABLE[] = {
316 { XK_space, RDP_SCANCODE_SPACE },
317 { XK_apostrophe, RDP_SCANCODE_OEM_7 },
318 { XK_comma, RDP_SCANCODE_OEM_COMMA },
319 { XK_minus, RDP_SCANCODE_OEM_MINUS },
320 { XK_period, RDP_SCANCODE_OEM_PERIOD },
321 { XK_slash, RDP_SCANCODE_OEM_2 },
322 { XK_0, RDP_SCANCODE_KEY_0 },
323 { XK_1, RDP_SCANCODE_KEY_1 },
324 { XK_2, RDP_SCANCODE_KEY_2 },
325 { XK_3, RDP_SCANCODE_KEY_3 },
326 { XK_4, RDP_SCANCODE_KEY_4 },
327 { XK_5, RDP_SCANCODE_KEY_5 },
328 { XK_6, RDP_SCANCODE_KEY_6 },
329 { XK_7, RDP_SCANCODE_KEY_7 },
330 { XK_8, RDP_SCANCODE_KEY_8 },
331 { XK_9, RDP_SCANCODE_KEY_9 },
332 { XK_semicolon, RDP_SCANCODE_OEM_1 },
333 { XK_less, RDP_SCANCODE_OEM_102 },
334 { XK_equal, RDP_SCANCODE_OEM_PLUS },
335 { XK_A, RDP_SCANCODE_KEY_A },
336 { XK_B, RDP_SCANCODE_KEY_B },
337 { XK_C, RDP_SCANCODE_KEY_C },
338 { XK_D, RDP_SCANCODE_KEY_D },
339 { XK_E, RDP_SCANCODE_KEY_E },
340 { XK_F, RDP_SCANCODE_KEY_F },
341 { XK_G, RDP_SCANCODE_KEY_G },
342 { XK_H, RDP_SCANCODE_KEY_H },
343 { XK_I, RDP_SCANCODE_KEY_I },
344 { XK_J, RDP_SCANCODE_KEY_J },
345 { XK_K, RDP_SCANCODE_KEY_K },
346 { XK_L, RDP_SCANCODE_KEY_L },
347 { XK_M, RDP_SCANCODE_KEY_M },
348 { XK_N, RDP_SCANCODE_KEY_N },
349 { XK_O, RDP_SCANCODE_KEY_O },
350 { XK_P, RDP_SCANCODE_KEY_P },
351 { XK_Q, RDP_SCANCODE_KEY_Q },
352 { XK_R, RDP_SCANCODE_KEY_R },
353 { XK_S, RDP_SCANCODE_KEY_S },
354 { XK_T, RDP_SCANCODE_KEY_T },
355 { XK_U, RDP_SCANCODE_KEY_U },
356 { XK_V, RDP_SCANCODE_KEY_V },
357 { XK_W, RDP_SCANCODE_KEY_W },
358 { XK_X, RDP_SCANCODE_KEY_X },
359 { XK_Y, RDP_SCANCODE_KEY_Y },
360 { XK_Z, RDP_SCANCODE_KEY_Z },
361 { XK_bracketleft, RDP_SCANCODE_OEM_4 },
362 { XK_backslash, RDP_SCANCODE_OEM_5 },
363 { XK_bracketright, RDP_SCANCODE_OEM_6 },
364 { XK_grave, RDP_SCANCODE_OEM_3 },
365 { XK_a, RDP_SCANCODE_KEY_A },
366 { XK_b, RDP_SCANCODE_KEY_B },
367 { XK_c, RDP_SCANCODE_KEY_C },
368 { XK_d, RDP_SCANCODE_KEY_D },
369 { XK_e, RDP_SCANCODE_KEY_E },
370 { XK_f, RDP_SCANCODE_KEY_F },
371 { XK_g, RDP_SCANCODE_KEY_G },
372 { XK_h, RDP_SCANCODE_KEY_H },
373 { XK_i, RDP_SCANCODE_KEY_I },
374 { XK_j, RDP_SCANCODE_KEY_J },
375 { XK_k, RDP_SCANCODE_KEY_K },
376 { XK_l, RDP_SCANCODE_KEY_L },
377 { XK_m, RDP_SCANCODE_KEY_M },
378 { XK_n, RDP_SCANCODE_KEY_N },
379 { XK_o, RDP_SCANCODE_KEY_O },
380 { XK_p, RDP_SCANCODE_KEY_P },
381 { XK_q, RDP_SCANCODE_KEY_Q },
382 { XK_r, RDP_SCANCODE_KEY_R },
383 { XK_s, RDP_SCANCODE_KEY_S },
384 { XK_t, RDP_SCANCODE_KEY_T },
385 { XK_u, RDP_SCANCODE_KEY_U },
386 { XK_v, RDP_SCANCODE_KEY_V },
387 { XK_w, RDP_SCANCODE_KEY_W },
388 { XK_x, RDP_SCANCODE_KEY_X },
389 { XK_y, RDP_SCANCODE_KEY_Y },
390 { XK_z, RDP_SCANCODE_KEY_Z },
391 { XK_ISO_Level3_Shift, RDP_SCANCODE_RMENU },
392 { XK_ISO_Left_Tab, RDP_SCANCODE_TAB },
393 { XK_BackSpace, RDP_SCANCODE_BACKSPACE },
394 { XK_Tab, RDP_SCANCODE_TAB },
395 { XK_Return, RDP_SCANCODE_RETURN },
396 { XK_Pause, RDP_SCANCODE_PAUSE },
397 { XK_Scroll_Lock, RDP_SCANCODE_SCROLLLOCK },
398 { XK_Escape, RDP_SCANCODE_ESCAPE },
399 { XK_Home, RDP_SCANCODE_HOME },
400 { XK_Left, RDP_SCANCODE_LEFT },
401 { XK_Up, RDP_SCANCODE_UP },
402 { XK_Right, RDP_SCANCODE_RIGHT },
403 { XK_Down, RDP_SCANCODE_DOWN },
404 { XK_Prior, RDP_SCANCODE_PRIOR },
405 { XK_Next, RDP_SCANCODE_NEXT },
406 { XK_End, RDP_SCANCODE_END },
407 { XK_Print, RDP_SCANCODE_PRINTSCREEN },
408 { XK_Insert, RDP_SCANCODE_INSERT },
409 { XK_Menu, RDP_SCANCODE_APPS },
410 { XK_Help, RDP_SCANCODE_HELP },
411 { XK_Mode_switch, RDP_SCANCODE_RMENU },
412 { XK_Num_Lock, RDP_SCANCODE_NUMLOCK },
413 { XK_KP_Enter, RDP_SCANCODE_RETURN_KP },
414 { XK_KP_Home, RDP_SCANCODE_NUMPAD7 },
415 { XK_KP_Left, RDP_SCANCODE_NUMPAD4 },
416 { XK_KP_Up, RDP_SCANCODE_NUMPAD8 },
417 { XK_KP_Right, RDP_SCANCODE_NUMPAD6 },
418 { XK_KP_Down, RDP_SCANCODE_NUMPAD2 },
419 { XK_KP_Prior, RDP_SCANCODE_NUMPAD9 },
420 { XK_KP_Next, RDP_SCANCODE_NUMPAD3 },
421 { XK_KP_End, RDP_SCANCODE_NUMPAD1 },
422 { XK_KP_Begin, RDP_SCANCODE_NUMPAD5 },
423 { XK_KP_Insert, RDP_SCANCODE_NUMPAD0 },
424 { XK_KP_Delete, RDP_SCANCODE_DECIMAL },
425 { XK_KP_Multiply, RDP_SCANCODE_MULTIPLY },
426 { XK_KP_Add, RDP_SCANCODE_ADD },
427 { XK_KP_Separator, RDP_SCANCODE_DECIMAL },
428 { XK_KP_Subtract, RDP_SCANCODE_SUBTRACT },
429 { XK_KP_Decimal, RDP_SCANCODE_DECIMAL },
430 { XK_KP_Divide, RDP_SCANCODE_DIVIDE },
431 { XK_KP_0, RDP_SCANCODE_NUMPAD0 },
432 { XK_KP_1, RDP_SCANCODE_NUMPAD1 },
433 { XK_KP_2, RDP_SCANCODE_NUMPAD2 },
434 { XK_KP_3, RDP_SCANCODE_NUMPAD3 },
435 { XK_KP_4, RDP_SCANCODE_NUMPAD4 },
436 { XK_KP_5, RDP_SCANCODE_NUMPAD5 },
437 { XK_KP_6, RDP_SCANCODE_NUMPAD6 },
438 { XK_KP_7, RDP_SCANCODE_NUMPAD7 },
439 { XK_KP_8, RDP_SCANCODE_NUMPAD8 },
440 { XK_KP_9, RDP_SCANCODE_NUMPAD9 },
441 { XK_F1, RDP_SCANCODE_F1 },
442 { XK_F2, RDP_SCANCODE_F2 },
443 { XK_F3, RDP_SCANCODE_F3 },
444 { XK_F4, RDP_SCANCODE_F4 },
445 { XK_F5, RDP_SCANCODE_F5 },
446 { XK_F6, RDP_SCANCODE_F6 },
447 { XK_F7, RDP_SCANCODE_F7 },
448 { XK_F8, RDP_SCANCODE_F8 },
449 { XK_F9, RDP_SCANCODE_F9 },
450 { XK_F10, RDP_SCANCODE_F10 },
451 { XK_F11, RDP_SCANCODE_F11 },
452 { XK_F12, RDP_SCANCODE_F12 },
453 { XK_F13, RDP_SCANCODE_F13 },
454 { XK_F14, RDP_SCANCODE_F14 },
455 { XK_F15, RDP_SCANCODE_F15 },
456 { XK_F16, RDP_SCANCODE_F16 },
457 { XK_F17, RDP_SCANCODE_F17 },
458 { XK_F18, RDP_SCANCODE_F18 },
459 { XK_F19, RDP_SCANCODE_F19 },
460 { XK_F20, RDP_SCANCODE_F20 },
461 { XK_F21, RDP_SCANCODE_F21 },
462 { XK_F22, RDP_SCANCODE_F22 },
463 { XK_F23, RDP_SCANCODE_F23 },
464 { XK_F24, RDP_SCANCODE_F24 },
465 { XK_Shift_L, RDP_SCANCODE_LSHIFT },
466 { XK_Shift_R, RDP_SCANCODE_RSHIFT },
467 { XK_Control_L, RDP_SCANCODE_LCONTROL },
468 { XK_Control_R, RDP_SCANCODE_RCONTROL },
469 { XK_Caps_Lock, RDP_SCANCODE_CAPSLOCK },
470 { XK_Meta_L, RDP_SCANCODE_LWIN },
471 { XK_Meta_R, RDP_SCANCODE_RWIN },
472 { XK_Alt_L, RDP_SCANCODE_LMENU },
473 { XK_Alt_R, RDP_SCANCODE_RMENU },
474 { XK_Super_L, RDP_SCANCODE_LWIN },
475 { XK_Super_R, RDP_SCANCODE_RWIN },
476 { XK_Hyper_L, RDP_SCANCODE_LWIN },
477 { XK_Hyper_R, RDP_SCANCODE_RWIN },
478 { XK_Delete, RDP_SCANCODE_DELETE }
479};
480
481static UINT32 xf_keyboard_get_toggle_keys_state(xfContext* xfc);
482static BOOL xf_keyboard_handle_special_keys(xfContext* xfc, KeySym keysym);
483static void xf_keyboard_handle_special_keys_release(xfContext* xfc, KeySym keysym);
484
485static void xf_keyboard_modifier_map_free(xfContext* xfc)
486{
487 WINPR_ASSERT(xfc);
488 if (xfc->modifierMap)
489 {
490 XFreeModifiermap(xfc->modifierMap);
491 xfc->modifierMap = nullptr;
492 }
493}
494
495BOOL xf_keyboard_update_modifier_map(xfContext* xfc)
496{
497 WINPR_ASSERT(xfc);
498 xf_keyboard_modifier_map_free(xfc);
499 xfc->modifierMap = XGetModifierMapping(xfc->display);
500 return xfc->modifierMap != nullptr;
501}
502
503static BOOL xf_keyboard_send_key(xfContext* xfc, BOOL down, BOOL repeat, const XKeyEvent* ev);
504static DWORD get_rdp_scancode_from_x11_keycode(xfContext* xfc, DWORD keycode);
505static int xf_keyboard_get_keymask(xfContext* xfc, KeySym keysym);
506
507static BOOL xf_sync_kbd_state(xfContext* xfc)
508{
509 const UINT32 syncFlags = xf_keyboard_get_toggle_keys_state(xfc);
510
511 WINPR_ASSERT(xfc);
512 return freerdp_input_send_synchronize_event(xfc->common.context.input, syncFlags);
513}
514
515static void xf_keyboard_clear(xfContext* xfc)
516{
517 WINPR_ASSERT(xfc);
518 ZeroMemory(xfc->KeyboardState, sizeof(xfc->KeyboardState));
519}
520
521static BOOL xf_keyboard_mapping_is_set(const xfContext* xfc)
522{
523 WINPR_ASSERT(xfc);
524
525 for (size_t i = 0; i < ARRAYSIZE(xfc->X11_KEYCODE_TO_VIRTUAL_SCANCODE); i++)
526 {
527 if (xfc->X11_KEYCODE_TO_VIRTUAL_SCANCODE[i] != RDP_SCANCODE_UNKNOWN)
528 return TRUE;
529 }
530
531 return FALSE;
532}
533
534static void xf_keyboard_clear_mapping(xfContext* xfc)
535{
536 WINPR_ASSERT(xfc);
537 ZeroMemory(xfc->X11_KEYCODE_TO_VIRTUAL_SCANCODE, sizeof(xfc->X11_KEYCODE_TO_VIRTUAL_SCANCODE));
538}
539
540static BOOL xf_action_script_append(xfContext* xfc, const char* buffer, size_t size,
541 WINPR_ATTR_UNUSED void* user, const char* what, const char* arg)
542{
543 WINPR_ASSERT(xfc);
544 WINPR_UNUSED(what);
545 WINPR_UNUSED(arg);
546
547 if (!buffer || (size == 0))
548 return TRUE;
549 return ArrayList_Append(xfc->keyCombinations, buffer);
550}
551
552static void xf_keyboard_action_script_free(xfContext* xfc)
553{
554 xf_event_action_script_free(xfc);
555
556 if (xfc->keyCombinations)
557 {
558 ArrayList_Free(xfc->keyCombinations);
559 xfc->keyCombinations = nullptr;
560 xfc->actionScriptExists = FALSE;
561 }
562}
563
564BOOL xf_keyboard_action_script_init(xfContext* xfc)
565{
566 WINPR_ASSERT(xfc);
567
568 xf_keyboard_action_script_free(xfc);
569 xfc->keyCombinations = ArrayList_New(TRUE);
570
571 if (!xfc->keyCombinations)
572 return FALSE;
573
574 wObject* obj = ArrayList_Object(xfc->keyCombinations);
575 WINPR_ASSERT(obj);
576 obj->fnObjectNew = winpr_ObjectStringClone;
577 obj->fnObjectFree = winpr_ObjectStringFree;
578
579 if (!run_action_script(xfc, "key", nullptr, xf_action_script_append, nullptr))
580 return FALSE;
581
582 return xf_event_action_script_init(xfc);
583}
584
585static int xkb_cmp(const void* pva, const void* pvb)
586{
587 const struct x11_key_scancode_t* a = pva;
588 const struct x11_key_scancode_t* b = pvb;
589
590 if (!a && !b)
591 return 0;
592 if (!a)
593 return 1;
594 if (!b)
595 return -1;
596 return strcmp(a->name, b->name);
597}
598
599static int keysym_cmp(const void* pva, const void* pvb)
600{
601 const x11_keysym_scancode_t* a = pva;
602 const x11_keysym_scancode_t* b = pvb;
603
604 if (!a && !b)
605 return 0;
606 if (!a)
607 return 1;
608 if (!b)
609 return -1;
610 if (a->keysym < b->keysym)
611 return -1;
612 if (a->keysym > b->keysym)
613 return 1;
614 return 0;
615}
616
617static BOOL try_add(xfContext* xfc, size_t offset, const char* xkb_keyname)
618{
619 WINPR_ASSERT(xfc);
620
621 struct x11_key_scancode_t key = { .name = xkb_keyname,
622 .sc = WINPR_ASSERTING_INT_CAST(uint32_t, offset) };
623
624 struct x11_key_scancode_t* found =
625 bsearch(&key, XKB_KEY_NAME_SCANCODE_TABLE, ARRAYSIZE(XKB_KEY_NAME_SCANCODE_TABLE),
626 sizeof(struct x11_key_scancode_t), xkb_cmp);
627 if (found)
628 {
629 WLog_Print(xfc->log, WLOG_DEBUG,
630 "%4s: keycode: 0x%02" PRIuz " -> rdp scancode: 0x%08" PRIx32 "", xkb_keyname,
631 offset, found->sc);
632 xfc->X11_KEYCODE_TO_VIRTUAL_SCANCODE[offset] = found->sc;
633 return TRUE;
634 }
635
636 return FALSE;
637}
638
639/* Keysym fallback for X11 forwarding where XKB key names don't match evdev */
640static DWORD xf_keysym_to_rdp_scancode(KeySym keysym)
641{
642 x11_keysym_scancode_t key = { .keysym = keysym, .sc = 0 };
643 x11_keysym_scancode_t* found =
644 bsearch(&key, KEYSYM_SCANCODE_TABLE, ARRAYSIZE(KEYSYM_SCANCODE_TABLE),
645 sizeof(x11_keysym_scancode_t), keysym_cmp);
646 if (found)
647 return found->sc;
648
649 return RDP_SCANCODE_UNKNOWN;
650}
651
652static int load_map_from_xkbfile(xfContext* xfc)
653{
654 WINPR_ASSERT(xfc);
655 int status = -1;
656
657 if (!xfc->display)
658 return -2;
659
660 XkbDescPtr xkb = XkbGetMap(xfc->display, 0, XkbUseCoreKbd);
661 if (!xkb)
662 {
663 WLog_Print(xfc->log, WLOG_WARN, "XkbGetMap() == nullptr");
664 return -3;
665 }
666
667 const int rc = XkbGetNames(xfc->display, XkbKeyNamesMask, xkb);
668 if (rc != Success)
669 {
670 char buffer[64] = WINPR_C_ARRAY_INIT;
671 WLog_Print(xfc->log, WLOG_WARN, "XkbGetNames() != Success: [%s]",
672 x11_error_to_string(xfc, rc, buffer, sizeof(buffer)));
673 }
674 else
675 {
676 char xkb_keyname[XkbKeyNameLength + 1] = { 42, 42, 42, 42,
677 0 }; /* end-of-string at index 5 */
678
679 WLog_Print(xfc->log, WLOG_TRACE, "XkbGetNames() == Success, min=%" PRIu8 ", max=%" PRIu8,
680 xkb->min_key_code, xkb->max_key_code);
681 for (size_t i = xkb->min_key_code; i < xkb->max_key_code; i++)
682 {
683 BOOL found = FALSE;
684 strncpy(xkb_keyname, xkb->names->keys[i].name, XkbKeyNameLength);
685
686 WLog_Print(xfc->log, WLOG_TRACE, "KeyCode %" PRIuz " -> %s", i, xkb_keyname);
687 if (strnlen(xkb_keyname, ARRAYSIZE(xkb_keyname)) >= 1)
688 found = try_add(xfc, i, xkb_keyname);
689
690 if (!found)
691 {
692#if defined(__APPLE__)
693 const DWORD vkcode =
694 GetVirtualKeyCodeFromKeycode((UINT32)i - 8u, WINPR_KEYCODE_TYPE_APPLE);
695 xfc->X11_KEYCODE_TO_VIRTUAL_SCANCODE[i] =
696 GetVirtualScanCodeFromVirtualKeyCode(vkcode, WINPR_KBD_TYPE_IBM_ENHANCED);
697 found = TRUE;
698#endif
699 }
700 if (!found)
701 {
702 WLog_Print(xfc->log, WLOG_WARN,
703 "%4s: keycode: 0x%02" PRIx32 " -> no RDP scancode found", xkb_keyname,
704 WINPR_ASSERTING_INT_CAST(UINT32, i));
705 }
706 else
707 status = 0;
708 }
709 }
710
711 XkbFreeKeyboard(xkb, 0, 1);
712
713 return status;
714}
715
716/* Keysym-based fallback for unmapped keycodes (e.g. X11 forwarding) */
717static BOOL load_map_from_keysym(xfContext* xfc)
718{
719 WINPR_ASSERT(xfc);
720
721 if (!xfc->display)
722 return FALSE;
723
724 BOOL mapped = FALSE;
725 int min_kc = 0;
726 int max_kc = 0;
727 XDisplayKeycodes(xfc->display, &min_kc, &max_kc);
728
729 for (int i = min_kc; i <= max_kc; i++)
730 {
731 if ((i < 0) || ((size_t)i >= ARRAYSIZE(xfc->X11_KEYCODE_TO_VIRTUAL_SCANCODE)))
732 continue;
733
734 if (xfc->X11_KEYCODE_TO_VIRTUAL_SCANCODE[i] != RDP_SCANCODE_UNKNOWN)
735 continue;
736
737 const KeySym ks = XkbKeycodeToKeysym(xfc->display, (KeyCode)i, 0, 0);
738 if (ks == NoSymbol)
739 continue;
740
741 const DWORD sc = xf_keysym_to_rdp_scancode(ks);
742 if (sc != RDP_SCANCODE_UNKNOWN)
743 {
744 xfc->X11_KEYCODE_TO_VIRTUAL_SCANCODE[i] = sc;
745 mapped = TRUE;
746 WLog_Print(xfc->log, WLOG_DEBUG,
747 "keycode: 0x%02x -> keysym: 0x%04lx -> rdp scancode: 0x%08" PRIx32
748 " (keysym fallback)",
749 (unsigned)i, (unsigned long)ks, sc);
750 }
751 }
752
753 return mapped;
754}
755
756#if defined(WITH_VERBOSE_WINPR_ASSERT)
757static BOOL compareKeySym(const x11_keysym_scancode_t* a, size_t counta,
758 const x11_keysym_scancode_t* b, size_t countb)
759{
760 WINPR_ASSERT(a || (counta == 0));
761 WINPR_ASSERT(b || (countb == 0));
762
763 if (counta != countb)
764 return FALSE;
765
766 for (size_t x = 0; x < counta; x++)
767 {
768 const x11_keysym_scancode_t* ca = &a[x];
769 const x11_keysym_scancode_t* cb = &b[x];
770 if (keysym_cmp(ca, cb) != 0)
771 {
772 WLog_ERR(TAG, "%" PRIuz "\ta=%lx, should be %lx", x, ca->keysym, cb->keysym);
773 WLog_ERR(TAG, "KEYSYM_SCANCODE_TABLE is not properly sorted!");
774 return FALSE;
775 }
776 }
777 return TRUE;
778}
779
780static BOOL compareKey(const struct x11_key_scancode_t* a, size_t counta,
781 const struct x11_key_scancode_t* b, size_t countb)
782{
783 WINPR_ASSERT(a || (counta == 0));
784 WINPR_ASSERT(b || (countb == 0));
785
786 if (counta != countb)
787 return FALSE;
788
789 for (size_t x = 0; x < counta; x++)
790 {
791 const struct x11_key_scancode_t* ca = &a[x];
792 const struct x11_key_scancode_t* cb = &b[x];
793 if (xkb_cmp(ca, cb) != 0)
794 {
795 WLog_ERR(TAG, "%" PRIuz "\ta=%s [%" PRIu32 "], should be %s [%" PRIu32 "]", x, a->name,
796 a->sc, b->name, b->sc);
797 WLog_ERR(TAG, "XKB_KEY_NAME_SCANCODE_TABLE is not properly sorted!");
798 return FALSE;
799 }
800 }
801 return TRUE;
802}
803#endif
804
805BOOL xf_keyboard_init(xfContext* xfc)
806{
807 rdpSettings* settings = nullptr;
808 DWORD previous_mapping[ARRAYSIZE(xfc->X11_KEYCODE_TO_VIRTUAL_SCANCODE)] = WINPR_C_ARRAY_INIT;
809 BOOL previous_mapping_is_set = FALSE;
810
811 WINPR_ASSERT(xfc);
812
813/* When assertions are enabled assert the lists are sorted. */
814#if defined(WITH_VERBOSE_WINPR_ASSERT)
815 {
816 x11_keysym_scancode_t copy[ARRAYSIZE(KEYSYM_SCANCODE_TABLE)] = WINPR_C_ARRAY_INIT;
817 memcpy(copy, KEYSYM_SCANCODE_TABLE, sizeof(copy));
818 qsort(copy, ARRAYSIZE(copy), sizeof(x11_keysym_scancode_t), keysym_cmp);
819
820 if (!compareKeySym(KEYSYM_SCANCODE_TABLE, ARRAYSIZE(KEYSYM_SCANCODE_TABLE), copy,
821 ARRAYSIZE(copy)))
822 return FALSE;
823 }
824 {
825 struct x11_key_scancode_t copy[ARRAYSIZE(XKB_KEY_NAME_SCANCODE_TABLE)] = WINPR_C_ARRAY_INIT;
826 memcpy(copy, XKB_KEY_NAME_SCANCODE_TABLE, sizeof(copy));
827 qsort(copy, ARRAYSIZE(copy), sizeof(struct x11_key_scancode_t), xkb_cmp);
828
829 if (!compareKey(XKB_KEY_NAME_SCANCODE_TABLE, ARRAYSIZE(XKB_KEY_NAME_SCANCODE_TABLE), copy,
830 ARRAYSIZE(copy)))
831 return FALSE;
832 }
833#endif
834
835 settings = xfc->common.context.settings;
836 WINPR_ASSERT(settings);
837
838 xf_keyboard_clear(xfc);
839
840 /* Layout detection algorithm:
841 *
842 * 1. If one was supplied, use that one
843 * 2. Try to determine current X11 keyboard layout
844 * 3. Try to determine default layout for current system language
845 * 4. Fall back to ENGLISH_UNITED_STATES
846 */
847 UINT32 KeyboardLayout = freerdp_settings_get_uint32(settings, FreeRDP_KeyboardLayout);
848 if (KeyboardLayout == 0)
849 {
850 xf_detect_keyboard_layout_from_xkb(xfc->log, &KeyboardLayout);
851 if (KeyboardLayout == 0)
852 freerdp_detect_keyboard_layout_from_system_locale(&KeyboardLayout);
853 if (KeyboardLayout == 0)
854 KeyboardLayout = ENGLISH_UNITED_STATES;
855 if (!freerdp_settings_set_uint32(settings, FreeRDP_KeyboardLayout, KeyboardLayout))
856 return FALSE;
857 }
858
859 previous_mapping_is_set = xf_keyboard_mapping_is_set(xfc);
860 memcpy(previous_mapping, xfc->X11_KEYCODE_TO_VIRTUAL_SCANCODE, sizeof(previous_mapping));
861 xf_keyboard_clear_mapping(xfc);
862
863 const int rc = load_map_from_xkbfile(xfc);
864
865 const BOOL keysym_mapped = load_map_from_keysym(xfc);
866
867 if (rc != 0 && !keysym_mapped)
868 {
869 if (!previous_mapping_is_set)
870 return FALSE;
871
872 /* A MappingNotify may carry a keymap without names or usable keysyms.
873 * Keep the last working physical-key mapping instead of ending the RDP
874 * session just because the new map cannot be decoded. */
875 memcpy(xfc->X11_KEYCODE_TO_VIRTUAL_SCANCODE, previous_mapping,
876 sizeof(xfc->X11_KEYCODE_TO_VIRTUAL_SCANCODE));
877 WLog_Print(xfc->log, WLOG_WARN,
878 "Could not map the new X11 keyboard map, keeping the previous mapping");
879 }
880
881 return xf_keyboard_update_modifier_map(xfc);
882}
883
884void xf_keyboard_free(xfContext* xfc)
885{
886 xf_keyboard_modifier_map_free(xfc);
887 xf_keyboard_action_script_free(xfc);
888}
889
890/* Only ordinary Alt is deferred. Shift keeps the existing immediate path. */
891enum
892{
893 XF_ALT_LEFT = 0x01,
894 XF_ALT_RIGHT = 0x02
895};
896
897static UINT32 xf_keyboard_alt_mask(xfContext* xfc, KeyCode code)
898{
899 if (!code || (code == XKeysymToKeycode(xfc->display, XK_ISO_Level3_Shift)) ||
900 (code == XKeysymToKeycode(xfc->display, XK_Mode_switch)))
901 return 0;
902
903 const DWORD scancode = get_rdp_scancode_from_x11_keycode(xfc, code);
904 if ((code == XKeysymToKeycode(xfc->display, XK_Alt_L)) && (scancode == RDP_SCANCODE_LMENU))
905 return XF_ALT_LEFT;
906 if ((code == XKeysymToKeycode(xfc->display, XK_Alt_R)) && (scancode == RDP_SCANCODE_RMENU))
907 return XF_ALT_RIGHT;
908 return 0;
909}
910
911BOOL xf_keyboard_flush_deferred_alt(xfContext* xfc)
912{
913 if (!xfc->deferAlt)
914 return TRUE;
915
916 /* A fixed left/right order is sufficient; no modifier press queue is kept. */
917 const UINT32 masks[] = { XF_ALT_LEFT, XF_ALT_RIGHT };
918 for (size_t i = 0; i < ARRAYSIZE(masks); i++)
919 {
920 const UINT32 mask = masks[i];
921 if (!(xfc->deferredAlt.pending & mask))
922 continue;
923 const DWORD scancode = (mask == XF_ALT_LEFT) ? RDP_SCANCODE_LMENU : RDP_SCANCODE_RMENU;
924 if (!freerdp_input_send_keyboard_event_ex(xfc->common.context.input, TRUE, FALSE, scancode))
925 return FALSE;
926 xfc->deferredAlt.pending &= ~mask;
927 xfc->deferredAlt.sent |= mask;
928 }
929 return TRUE;
930}
931
932/* A key that another client has grabbed (for example mutter grabs the bare
933 * "locate pointer" modifier, Control_L by default) never reaches this client as
934 * a KeyPress event. Modifier state is tracked from the events that do arrive,
935 * so a grabbed modifier silently disappears from every combination sent to the
936 * remote application: it receives the plain key without the modifier.
937 *
938 * The X server still reports the physical key state, so compare the modifier
939 * mask carried by the event against the modifiers tracked here and forward any
940 * modifier that is physically pressed but was never seen pressed. */
941static void xf_keyboard_sync_pressed_modifiers(xfContext* xfc, UINT32 state, KeyCode skip)
942{
943 static const KeySym keysyms[] = { XK_Shift_L, XK_Shift_R, XK_Control_L, XK_Control_R,
944 XK_Alt_L, XK_Alt_R, XK_Super_L, XK_Super_R };
945 char keys[32] = WINPR_C_ARRAY_INIT;
946 int expected = 0;
947 int tracked = 0;
948
949 WINPR_ASSERT(xfc);
950
951 if (!xfc->display)
952 return;
953
954 for (size_t i = 0; i < ARRAYSIZE(keysyms); i++)
955 {
956 /* Alt is deferred or forwarded by the deferred Alt logic. */
957 if (xfc->deferAlt && ((keysyms[i] == XK_Alt_L) || (keysyms[i] == XK_Alt_R)))
958 continue;
959
960 WINPR_ASSERT(sizeof(KeyCode) == 1);
961 WINPR_STATIC_ASSERT(ARRAYSIZE(xfc->KeyboardState) == 256);
962 const KeyCode keycode = XKeysymToKeycode(xfc->display, keysyms[i]);
963 if (keycode == 0)
964 continue;
965
966 const int mask = xf_keyboard_get_keymask(xfc, keysyms[i]);
967 if (mask == 0)
968 continue;
969
970 expected |= mask;
971 if ((keycode != skip) && xfc->KeyboardState[keycode])
972 tracked |= mask;
973 }
974
975 /* Fast path: the modifier state carried by the event matches the tracked keys. */
976 if (((int)state & expected) == tracked)
977 return;
978
979 if (XQueryKeymap(xfc->display, keys) == 0)
980 return;
981
982 for (size_t i = 0; i < ARRAYSIZE(keysyms); i++)
983 {
984 if (xfc->deferAlt && ((keysyms[i] == XK_Alt_L) || (keysyms[i] == XK_Alt_R)))
985 continue;
986
987 WINPR_ASSERT(sizeof(KeyCode) == 1);
988 WINPR_STATIC_ASSERT(ARRAYSIZE(xfc->KeyboardState) == 256);
989 const KeyCode keycode = XKeysymToKeycode(xfc->display, keysyms[i]);
990 if ((keycode == 0) || (keycode == skip))
991 continue;
992 if (xfc->KeyboardState[keycode])
993 continue;
994
995 const int mask = xf_keyboard_get_keymask(xfc, keysyms[i]);
996 if ((mask != 0) && ((tracked & mask) == mask))
997 continue;
998
999 /* Only the keyboard reports the physical state, the event mask cannot tell
1000 * the left from the right key of the same modifier. */
1001 if ((keys[keycode / 8] & (1 << (keycode % 8))) == 0)
1002 continue;
1003
1004 const DWORD scancode = get_rdp_scancode_from_x11_keycode(xfc, keycode);
1005 if (scancode == 0)
1006 continue;
1007
1008 xfc->KeyboardState[keycode] = TRUE;
1009 if (!freerdp_input_send_keyboard_event_ex(xfc->common.context.input, TRUE, FALSE, scancode))
1010 xfc->KeyboardState[keycode] = FALSE;
1011 else
1012 tracked |= mask;
1013 }
1014}
1015
1016void xf_keyboard_key_press(xfContext* xfc, const XKeyEvent* event, KeySym keysym)
1017{
1018 WINPR_ASSERT(xfc);
1019 WINPR_ASSERT(event);
1020 WINPR_ASSERT(event->keycode < ARRAYSIZE(xfc->KeyboardState));
1021
1022 xf_keyboard_sync_pressed_modifiers(xfc, event->state, (KeyCode)event->keycode);
1023
1024 const BOOL defer = xfc->deferAlt;
1025 const BOOL last = xfc->KeyboardState[event->keycode];
1026 xfc->KeyboardState[event->keycode] = TRUE;
1027
1028 if (xf_keyboard_handle_special_keys(xfc, keysym))
1029 {
1030 if (defer)
1031 {
1032 xfc->deferredAlt.pending = 0;
1033 xfc->KeyboardState[event->keycode] = last;
1034 }
1035 return;
1036 }
1037 if (defer)
1038 {
1039 const UINT32 mask = xf_keyboard_alt_mask(xfc, (KeyCode)event->keycode);
1040 if (mask)
1041 {
1042 if (!(xfc->deferredAlt.sent & mask))
1043 {
1044 xfc->deferredAlt.pending |= mask;
1045 return;
1046 }
1047 const DWORD scancode = get_rdp_scancode_from_x11_keycode(xfc, event->keycode);
1048 (void)freerdp_input_send_keyboard_event_ex(xfc->common.context.input, TRUE, last,
1049 scancode);
1050 return;
1051 }
1052 /* Shift must not commit Alt before a host Alt+Shift+Tab shortcut. */
1053 if ((keysym != XK_Shift_L) && (keysym != XK_Shift_R) &&
1054 !xf_keyboard_flush_deferred_alt(xfc))
1055 {
1056 xfc->KeyboardState[event->keycode] = last;
1057 return;
1058 }
1059 }
1060 xf_keyboard_send_key(xfc, TRUE, last, event);
1061}
1062
1063void xf_keyboard_key_release(xfContext* xfc, const XKeyEvent* event, KeySym keysym)
1064{
1065 WINPR_ASSERT(xfc);
1066 WINPR_ASSERT(event);
1067 WINPR_ASSERT(event->keycode < ARRAYSIZE(xfc->KeyboardState));
1068
1069 const BOOL last = xfc->KeyboardState[event->keycode];
1070 xfc->KeyboardState[event->keycode] = FALSE;
1071 xf_keyboard_handle_special_keys_release(xfc, keysym);
1072 if (xfc->deferAlt)
1073 {
1074 const UINT32 mask = xf_keyboard_alt_mask(xfc, (KeyCode)event->keycode);
1075 if (mask)
1076 {
1077 /* An unmatched release after cancellation must not create an Alt tap. */
1078 if (xfc->deferredAlt.pending & mask)
1079 (void)xf_keyboard_flush_deferred_alt(xfc);
1080 if (xfc->deferredAlt.sent & mask)
1081 {
1082 const DWORD scancode = get_rdp_scancode_from_x11_keycode(xfc, event->keycode);
1083 (void)freerdp_input_send_keyboard_event_ex(xfc->common.context.input, FALSE, FALSE,
1084 scancode);
1085 }
1086 xfc->deferredAlt.pending &= ~mask;
1087 xfc->deferredAlt.sent &= ~mask;
1088 return;
1089 }
1090 if (!last)
1091 return;
1092 /* Shift release commits Alt before Windows interprets Alt+Shift. */
1093 if (((keysym == XK_Shift_L) || (keysym == XK_Shift_R)) &&
1094 !xf_keyboard_flush_deferred_alt(xfc))
1095 return;
1096 }
1097 xf_keyboard_send_key(xfc, FALSE, last, event);
1098}
1099
1100static DWORD get_rdp_scancode_from_x11_keycode(xfContext* xfc, DWORD keycode)
1101{
1102 WINPR_ASSERT(xfc);
1103 if (keycode >= ARRAYSIZE(xfc->X11_KEYCODE_TO_VIRTUAL_SCANCODE))
1104 {
1105 WLog_ERR(TAG, "KeyCode %" PRIu32 " exceeds allowed value range [0,%" PRIuz "]", keycode,
1106 ARRAYSIZE(xfc->X11_KEYCODE_TO_VIRTUAL_SCANCODE));
1107 return 0;
1108 }
1109
1110 const DWORD scancode = xfc->X11_KEYCODE_TO_VIRTUAL_SCANCODE[keycode];
1111 const DWORD remapped = freerdp_keyboard_remap_key(xfc->remap_table, scancode);
1112
1113#if defined(WITH_DEBUG_KBD)
1114 {
1115 const BOOL ex = RDP_SCANCODE_EXTENDED(scancode);
1116 const DWORD sc = RDP_SCANCODE_CODE(scancode);
1117 WLog_DBG(TAG, "x11 keycode: %02" PRIX32 " -> rdp code: [%04" PRIx16 "] %02" PRIX8 "%s",
1118 keycode, scancode, sc, ex ? " extended" : "");
1119 }
1120#endif
1121
1122 if (remapped != 0)
1123 {
1124#if defined(WITH_DEBUG_KBD)
1125 {
1126 const BOOL ex = RDP_SCANCODE_EXTENDED(remapped);
1127 const DWORD sc = RDP_SCANCODE_CODE(remapped);
1128 WLog_DBG(TAG,
1129 "x11 keycode: %02" PRIX32 " -> remapped rdp code: [%04" PRIx16 "] %02" PRIX8
1130 "%s",
1131 keycode, remapped, sc, ex ? " extended" : "");
1132 }
1133#endif
1134 return remapped;
1135 }
1136
1137 return scancode;
1138}
1139
1140static BOOL isModifier(DWORD scancode)
1141{
1142 const DWORD modifiers[] = { RDP_SCANCODE_LWIN, RDP_SCANCODE_RWIN, RDP_SCANCODE_LMENU,
1143 RDP_SCANCODE_RMENU, RDP_SCANCODE_LCONTROL, RDP_SCANCODE_RCONTROL,
1144 RDP_SCANCODE_APPS };
1145
1146 for (size_t x = 0; x < ARRAYSIZE(modifiers); x++)
1147 {
1148 if (modifiers[x] == scancode)
1149 return TRUE;
1150 }
1151
1152 return FALSE;
1153}
1154
1155static void release_keys(xfContext* xfc, BOOL modifiers)
1156{
1157 WINPR_ASSERT(xfc);
1158
1159 WINPR_STATIC_ASSERT(ARRAYSIZE(xfc->KeyboardState) <= UINT32_MAX);
1160 for (size_t keycode = 0; keycode < ARRAYSIZE(xfc->KeyboardState); keycode++)
1161 {
1162 if (xfc->KeyboardState[keycode])
1163 {
1164 /* Physical Alt may never have been sent to the remote. */
1165 if (xfc->deferAlt)
1166 {
1167 const UINT32 mask = xf_keyboard_alt_mask(xfc, (KeyCode)keycode);
1168 if (mask && !(xfc->deferredAlt.sent & mask))
1169 {
1170 xfc->KeyboardState[keycode] = FALSE;
1171 continue;
1172 }
1173 }
1174 const DWORD rdp_scancode =
1175 get_rdp_scancode_from_x11_keycode(xfc, WINPR_ASSERTING_INT_CAST(UINT32, keycode));
1176
1177 if (isModifier(rdp_scancode) == modifiers)
1178 {
1179 (void)freerdp_input_send_keyboard_event_ex(xfc->common.context.input, FALSE, FALSE,
1180 rdp_scancode);
1181 xfc->KeyboardState[keycode] = FALSE;
1182 }
1183 }
1184 }
1185}
1186
1187void xf_keyboard_release_all_keypress(xfContext* xfc)
1188{
1189 WINPR_ASSERT(xfc);
1190
1191 if (xfc->deferAlt)
1192 xfc->deferredAlt.pending = 0;
1193 release_keys(xfc, FALSE);
1194 release_keys(xfc, TRUE);
1195 xfc->deferredAlt.sent = 0;
1196}
1197
1198static BOOL xf_keyboard_key_pressed(xfContext* xfc, KeySym keysym)
1199{
1200 KeyCode keycode = XKeysymToKeycode(xfc->display, keysym);
1201 WINPR_ASSERT(keycode < ARRAYSIZE(xfc->KeyboardState));
1202 return xfc->KeyboardState[keycode];
1203}
1204
1205WINPR_ATTR_NODISCARD
1206static UINT32 xk_keyboard_get_modifier_keys(xfContext* xfc);
1207
1208static BOOL xf_keyboard_has_system_modifier(xfContext* xfc)
1209{
1210 WINPR_ASSERT(xfc);
1211
1212 const UINT32 mods = xk_keyboard_get_modifier_keys(xfc);
1213 return (mods & (FREERDP_CLIENT_KBD_MOD_LCONTROL | FREERDP_CLIENT_KBD_MOD_RCONTROL |
1214 FREERDP_CLIENT_KBD_MOD_LMENU | FREERDP_CLIENT_KBD_MOD_RMENU |
1215 FREERDP_CLIENT_KBD_MOD_LWIN | FREERDP_CLIENT_KBD_MOD_RWIN)) == 0;
1216}
1217
1218BOOL xf_keyboard_send_key(xfContext* xfc, BOOL down, BOOL repeat, const XKeyEvent* event)
1219{
1220 WINPR_ASSERT(xfc);
1221 WINPR_ASSERT(event);
1222
1223 rdpInput* input = xfc->common.context.input;
1224 WINPR_ASSERT(input);
1225
1226 const DWORD rdp_scancode = get_rdp_scancode_from_x11_keycode(xfc, event->keycode);
1227 if (rdp_scancode == RDP_SCANCODE_PAUSE && !xf_keyboard_key_pressed(xfc, XK_Control_L) &&
1228 !xf_keyboard_key_pressed(xfc, XK_Control_R))
1229 {
1230 /* Pause without Ctrl has to be sent as a series of keycodes
1231 * in a single input PDU. Pause only happens on "press";
1232 * no code is sent on "release".
1233 */
1234 if (down)
1235 return freerdp_input_send_keyboard_pause_event(input);
1236 }
1237 else
1238 {
1239 if (freerdp_settings_get_bool(xfc->common.context.settings, FreeRDP_UnicodeInput) &&
1240 !xf_keyboard_has_system_modifier(xfc))
1241 {
1242 wchar_t buffer[32] = WINPR_C_ARRAY_INIT;
1243 int xwc = -1;
1244 const UINT32 modmask = xk_keyboard_get_modifier_keys(xfc);
1245
1246 XIM xim = XOpenIM(xfc->display, nullptr, nullptr, nullptr);
1247 if (!xim)
1248 {
1249 WLog_WARN(TAG, "Failed to XOpenIM");
1250 }
1251 else
1252 {
1253 XIC xic =
1254 XCreateIC(xim, XNInputStyle, XIMPreeditNothing | XIMStatusNothing, nullptr);
1255 if (!xic)
1256 {
1257 WLog_WARN(TAG, "XCreateIC failed");
1258 }
1259 else
1260 {
1261 KeySym ignore = WINPR_C_ARRAY_INIT;
1262 Status return_status = 0;
1263 XKeyEvent ev = *event;
1264 ev.type = KeyPress;
1265 xwc = XwcLookupString(xic, &ev, buffer, ARRAYSIZE(buffer), &ignore,
1266 &return_status);
1267 XDestroyIC(xic);
1268 }
1269 XCloseIM(xim);
1270 }
1271
1272 char str[3 * ARRAYSIZE(buffer)] = WINPR_C_ARRAY_INIT;
1273 if (xwc > 0)
1274 {
1275 // NOLINTNEXTLINE(concurrency-mt-unsafe)
1276 (void)wcstombs(str, buffer, ARRAYSIZE(buffer));
1277 }
1278 return freerdp_client_send_key_as_unicode(&xfc->common, down ? 0 : KBD_FLAGS_RELEASE,
1279 str, rdp_scancode, modmask);
1280 }
1281 else if (rdp_scancode == RDP_SCANCODE_UNKNOWN)
1282 WLog_ERR(TAG, "Unknown key with X keycode 0x%02" PRIx8 "", event->keycode);
1283 else
1284 return freerdp_input_send_keyboard_event_ex(input, down, repeat, rdp_scancode);
1285 }
1286 return FALSE;
1287}
1288
1289static int xf_keyboard_read_keyboard_state(xfContext* xfc)
1290{
1291 int dummy = 0;
1292 Window wdummy = 0;
1293 UINT32 state = 0;
1294
1295 if (!xfc->remote_app && xfc->window)
1296 {
1297 XQueryPointer(xfc->display, xfc->window->handle, &wdummy, &wdummy, &dummy, &dummy, &dummy,
1298 &dummy, &state);
1299 }
1300 else
1301 {
1302 XQueryPointer(xfc->display, DefaultRootWindow(xfc->display), &wdummy, &wdummy, &dummy,
1303 &dummy, &dummy, &dummy, &state);
1304 }
1305
1306 return WINPR_ASSERTING_INT_CAST(int, state);
1307}
1308
1309static int xf_keyboard_get_keymask(xfContext* xfc, KeySym keysym)
1310{
1311 int keysymMask = 0;
1312 KeyCode keycode = XKeysymToKeycode(xfc->display, keysym);
1313
1314 if (keycode == NoSymbol)
1315 return 0;
1316
1317 WINPR_ASSERT(xfc->modifierMap);
1318 for (int modifierpos = 0; modifierpos < 8; modifierpos++)
1319 {
1320 int offset = xfc->modifierMap->max_keypermod * modifierpos;
1321
1322 for (int key = 0; key < xfc->modifierMap->max_keypermod; key++)
1323 {
1324 if (xfc->modifierMap->modifiermap[offset + key] == keycode)
1325 {
1326 keysymMask |= 1 << modifierpos;
1327 }
1328 }
1329 }
1330
1331 return keysymMask;
1332}
1333
1334static BOOL xf_keyboard_get_key_state(xfContext* xfc, int state, KeySym keysym)
1335{
1336 int keysymMask = xf_keyboard_get_keymask(xfc, keysym);
1337
1338 if (!keysymMask)
1339 return FALSE;
1340
1341 return (state & keysymMask) != 0;
1342}
1343
1344static BOOL xf_keyboard_set_key_state(xfContext* xfc, BOOL on, KeySym keysym)
1345{
1346 if (!xfc->xkbAvailable)
1347 return FALSE;
1348
1349 const int keysymMask = xf_keyboard_get_keymask(xfc, keysym);
1350
1351 if (!keysymMask)
1352 {
1353 return FALSE;
1354 }
1355
1356 return XkbLockModifiers(xfc->display, XkbUseCoreKbd,
1357 WINPR_ASSERTING_INT_CAST(uint32_t, keysymMask),
1358 on ? WINPR_ASSERTING_INT_CAST(uint32_t, keysymMask) : 0);
1359}
1360
1361UINT32 xf_keyboard_get_toggle_keys_state(xfContext* xfc)
1362{
1363 UINT32 toggleKeysState = 0;
1364 const int state = xf_keyboard_read_keyboard_state(xfc);
1365
1366 if (xf_keyboard_get_key_state(xfc, state, XK_Scroll_Lock))
1367 toggleKeysState |= KBD_SYNC_SCROLL_LOCK;
1368
1369 if (xf_keyboard_get_key_state(xfc, state, XK_Num_Lock))
1370 toggleKeysState |= KBD_SYNC_NUM_LOCK;
1371
1372 if (xf_keyboard_get_key_state(xfc, state, XK_Caps_Lock))
1373 toggleKeysState |= KBD_SYNC_CAPS_LOCK;
1374
1375 if (xf_keyboard_get_key_state(xfc, state, XK_Kana_Lock))
1376 toggleKeysState |= KBD_SYNC_KANA_LOCK;
1377
1378 return toggleKeysState;
1379}
1380
1381static void xk_keyboard_update_modifier_keys(xfContext* xfc)
1382{
1383 const KeySym keysyms[] = { XK_Shift_L, XK_Shift_R, XK_Alt_L, XK_Alt_R,
1384 XK_Control_L, XK_Control_R, XK_Super_L, XK_Super_R };
1385
1386 xf_keyboard_clear(xfc);
1387 if (xfc->deferAlt)
1388 {
1389 /* Focus return starts a new sequence; do not restore held modifiers. */
1390 xfc->deferredAlt.pending = 0;
1391 xfc->deferredAlt.sent = 0;
1392 return;
1393 }
1394
1395 const int state = xf_keyboard_read_keyboard_state(xfc);
1396
1397 for (size_t i = 0; i < ARRAYSIZE(keysyms); i++)
1398 {
1399 if (xf_keyboard_get_key_state(xfc, state, keysyms[i]))
1400 {
1401 const KeyCode keycode = XKeysymToKeycode(xfc->display, keysyms[i]);
1402 WINPR_ASSERT(keycode < ARRAYSIZE(xfc->KeyboardState));
1403 xfc->KeyboardState[keycode] = TRUE;
1404 }
1405 }
1406}
1407
1408void xf_keyboard_focus_in(xfContext* xfc)
1409{
1410 UINT32 state = 0;
1411 Window w = None;
1412 int d = 0;
1413 int x = 0;
1414 int y = 0;
1415
1416 WINPR_ASSERT(xfc);
1417 if (!xfc->display || !xfc->window)
1418 return;
1419
1420 rdpInput* input = xfc->common.context.input;
1421 WINPR_ASSERT(input);
1422
1423 const UINT32 syncFlags = xf_keyboard_get_toggle_keys_state(xfc);
1424 freerdp_input_send_focus_in_event(input, WINPR_ASSERTING_INT_CAST(UINT16, syncFlags));
1425 xk_keyboard_update_modifier_keys(xfc);
1426
1427 /* finish with a mouse pointer position like mstsc.exe if required */
1428
1429 if (xfc->remote_app || !xfc->window)
1430 return;
1431
1432 if (XQueryPointer(xfc->display, xfc->window->handle, &w, &w, &d, &d, &x, &y, &state))
1433 {
1434 if ((x >= 0) && (x < xfc->window->width) && (y >= 0) && (y < xfc->window->height))
1435 {
1436 xf_event_adjust_coordinates(xfc, &x, &y);
1437 freerdp_client_send_button_event(&xfc->common, FALSE, PTR_FLAGS_MOVE, x, y);
1438 }
1439 }
1440}
1441
1442static BOOL action_script_run(xfContext* xfc, const char* buffer, size_t size, void* user,
1443 const char* what, const char* arg)
1444{
1445 WINPR_UNUSED(xfc);
1446 WINPR_UNUSED(what);
1447 WINPR_UNUSED(arg);
1448 WINPR_ASSERT(user);
1449 int* pstatus = user;
1450
1451 if (size == 0)
1452 {
1453 WLog_WARN(TAG, "ActionScript key: script did not return data");
1454 return FALSE;
1455 }
1456
1457 if (strcmp(buffer, "key-local") == 0)
1458 *pstatus = 0;
1459 else if (winpr_PathFileExists(buffer))
1460 {
1461 // NOLINTNEXTLINE(bugprone-command-processor)
1462 FILE* fp = popen(buffer, "w");
1463 if (!fp)
1464 {
1465 WLog_ERR(TAG, "Failed to execute '%s'", buffer);
1466 return FALSE;
1467 }
1468
1469 *pstatus = pclose(fp);
1470 if (*pstatus < 0)
1471 {
1472 WLog_ERR(TAG, "Command '%s' returned %d", buffer, *pstatus);
1473 return FALSE;
1474 }
1475 }
1476 else
1477 {
1478 WLog_WARN(TAG, "ActionScript key: no such file '%s'", buffer);
1479 return FALSE;
1480 }
1481 return TRUE;
1482}
1483
1484static int xf_keyboard_execute_action_script(xfContext* xfc, UINT32 mods, KeySym keysym)
1485{
1486 int status = 1;
1487 BOOL match = FALSE;
1488 char command[2048] = WINPR_C_ARRAY_INIT;
1489 char combination[1024] = WINPR_C_ARRAY_INIT;
1490
1491 if (!xfc->actionScriptExists)
1492 return 1;
1493
1494 if ((keysym == XK_Shift_L) || (keysym == XK_Shift_R) || (keysym == XK_Alt_L) ||
1495 (keysym == XK_Alt_R) || (keysym == XK_Control_L) || (keysym == XK_Control_R))
1496 {
1497 return 1;
1498 }
1499
1500 const char* keyStr = XKeysymToString(keysym);
1501
1502 if (keyStr == nullptr)
1503 {
1504 return 1;
1505 }
1506
1507 if ((mods & (FREERDP_CLIENT_KBD_MOD_LSHIFT | FREERDP_CLIENT_KBD_MOD_RSHIFT)) != 0)
1508 winpr_str_append("Shift", combination, sizeof(combination), "+");
1509
1510 if ((mods & (FREERDP_CLIENT_KBD_MOD_LCONTROL | FREERDP_CLIENT_KBD_MOD_RCONTROL)) != 0)
1511 winpr_str_append("Ctrl", combination, sizeof(combination), "+");
1512
1513 if ((mods & (FREERDP_CLIENT_KBD_MOD_LMENU | FREERDP_CLIENT_KBD_MOD_RMENU)) != 0)
1514 winpr_str_append("Alt", combination, sizeof(combination), "+");
1515
1516 if ((mods & (FREERDP_CLIENT_KBD_MOD_LWIN | FREERDP_CLIENT_KBD_MOD_RWIN)) != 0)
1517 winpr_str_append("Super", combination, sizeof(combination), "+");
1518
1519 winpr_str_append(keyStr, combination, sizeof(combination), "+");
1520
1521 for (size_t i = 0; i < strnlen(combination, sizeof(combination)); i++)
1522 combination[i] = WINPR_ASSERTING_INT_CAST(char, tolower(combination[i]));
1523
1524 const size_t count = ArrayList_Count(xfc->keyCombinations);
1525
1526 for (size_t index = 0; index < count; index++)
1527 {
1528 const char* keyCombination = (const char*)ArrayList_GetItem(xfc->keyCombinations, index);
1529
1530 if (_stricmp(keyCombination, combination) == 0)
1531 {
1532 match = TRUE;
1533 break;
1534 }
1535 }
1536
1537 if (!match)
1538 return 1;
1539
1540 (void)sprintf_s(command, sizeof(command), "key %s", combination);
1541 if (!run_action_script(xfc, command, nullptr, action_script_run, &status))
1542 return -1;
1543
1544 return status;
1545}
1546
1547UINT32 xk_keyboard_get_modifier_keys(xfContext* xfc)
1548{
1549 UINT32 mods = FREERDP_CLIENT_KBD_MOD_NONE;
1550
1551 if (xf_keyboard_key_pressed(xfc, XK_Shift_L))
1552 mods |= FREERDP_CLIENT_KBD_MOD_LSHIFT;
1553 if (xf_keyboard_key_pressed(xfc, XK_Shift_R))
1554 mods |= FREERDP_CLIENT_KBD_MOD_RSHIFT;
1555 if (xf_keyboard_key_pressed(xfc, XK_Alt_L))
1556 mods |= FREERDP_CLIENT_KBD_MOD_LMENU;
1557 if (xf_keyboard_key_pressed(xfc, XK_Alt_R))
1558 mods |= FREERDP_CLIENT_KBD_MOD_RMENU;
1559 if (xf_keyboard_key_pressed(xfc, XK_Control_L))
1560 mods |= FREERDP_CLIENT_KBD_MOD_LCONTROL;
1561 if (xf_keyboard_key_pressed(xfc, XK_Control_R))
1562 mods |= FREERDP_CLIENT_KBD_MOD_RCONTROL;
1563 if (xf_keyboard_key_pressed(xfc, XK_Super_L))
1564 mods |= FREERDP_CLIENT_KBD_MOD_LWIN;
1565 if (xf_keyboard_key_pressed(xfc, XK_Super_R))
1566 mods |= FREERDP_CLIENT_KBD_MOD_RWIN;
1567 return mods;
1568}
1569
1570BOOL xf_keyboard_handle_special_keys(xfContext* xfc, KeySym keysym)
1571{
1572 const UINT32 mods = xk_keyboard_get_modifier_keys(xfc);
1573
1574 // remember state of RightCtrl to ungrab keyboard if next action is release of RightCtrl
1575 // do not return anything such that the key could be used by client if ungrab is not the goal
1576 if (keysym == XK_Control_R)
1577 {
1578 const BOOL rctrl = (mods & FREERDP_CLIENT_KBD_MOD_RCONTROL) != 0;
1579 if (rctrl && !xfc->wasRightCtrlAlreadyPressed)
1580 {
1581 // Right Ctrl is pressed, getting ready to ungrab
1582 xfc->ungrabKeyboardWithRightCtrl = TRUE;
1583 xfc->wasRightCtrlAlreadyPressed = TRUE;
1584 }
1585 }
1586 else
1587 {
1588 // some other key has been pressed, abort ungrabbing
1589 if (xfc->ungrabKeyboardWithRightCtrl)
1590 xfc->ungrabKeyboardWithRightCtrl = FALSE;
1591 }
1592
1593 const int rc = xf_keyboard_execute_action_script(xfc, mods, keysym);
1594 if (rc < 0)
1595 return FALSE;
1596 if (rc == 0)
1597 return TRUE;
1598
1599 const BOOL ctrl =
1600 (mods & (FREERDP_CLIENT_KBD_MOD_LCONTROL | FREERDP_CLIENT_KBD_MOD_RCONTROL)) != 0;
1601 const BOOL alt = (mods & (FREERDP_CLIENT_KBD_MOD_LMENU | FREERDP_CLIENT_KBD_MOD_RMENU)) != 0;
1602 if (!xfc->remote_app && xfc->fullscreen_toggle)
1603 {
1604 switch (keysym)
1605 {
1606 case XK_Return:
1607 if (ctrl && alt)
1608 {
1609 /* Ctrl-Alt-Enter: toggle full screen */
1610 WLog_INFO(TAG, "<ctrl>+<alt>+<enter> pressed, toggling fullscreen state...");
1611 xf_sync_kbd_state(xfc);
1612 xf_toggle_fullscreen(xfc);
1613 return TRUE;
1614 }
1615 break;
1616 default:
1617 break;
1618 }
1619 }
1620
1621 if (ctrl && alt)
1622 {
1623 switch (keysym)
1624 {
1625 case XK_m:
1626 case XK_M:
1627 WLog_INFO(TAG, "<ctrl>+<alt>+m pressed, minimizing RDP session...");
1628 xf_sync_kbd_state(xfc);
1629 xf_minimize(xfc);
1630 return TRUE;
1631 case XK_c:
1632 case XK_C:
1633 /* Ctrl-Alt-C: toggle control */
1634 WLog_INFO(TAG, "<ctrl>+<alt>+c pressed, toggle encomps control...");
1635 if (freerdp_client_encomsp_toggle_control(xfc->common.encomsp))
1636 return TRUE;
1637 break;
1638 case XK_d:
1639 case XK_D:
1640 /* <ctrl>+<alt>+d: disconnect session */
1641 WLog_INFO(TAG, "<ctrl>+<alt>+d pressed, terminating RDP session...");
1642 xf_sync_kbd_state(xfc);
1643 return freerdp_abort_connect_context(&xfc->common.context);
1644
1645 default:
1646 break;
1647 }
1648 }
1649
1650#if defined(WITH_X11_MULTITOUCH_EMULATION) /* set to 1 to enable multi touch gesture simulation \
1651 via keyboard */
1652#ifdef WITH_XRENDER
1653
1654 if (!xfc->remote_app && freerdp_settings_get_bool(xfc->common.context.settings, FreeRDP_MultiTouchGestures))
1655 {
1656 rdpContext* ctx = &xfc->common.context;
1657
1658 if (mod.Ctrl && mod.Alt)
1659 {
1660 int pdx = 0;
1661 int pdy = 0;
1662 int zdx = 0;
1663 int zdy = 0;
1664
1665 switch (keysym)
1666 {
1667 case XK_0: /* Ctrl-Alt-0: Reset scaling and panning */{
1668 const UINT32 sessionWidth = freerdp_settings_get_uint32(xfc->common.context.settings, FreeRDP_DesktopWidth);
1669 const UINT32 sessionHeight = freerdp_settings_get_uint32(xfc->common.context.settings, FreeRDP_DesktopHeight);
1670
1671 xfc->scaledWidth = sessionWidth;
1672 xfc->scaledHeight = sessionHeight;
1673 xfc->offset_x = 0;
1674 xfc->offset_y = 0;
1675
1676 if (!xfc->fullscreen && (sessionWidth != xfc->window->width ||
1677 sessionHeight != xfc->window->height))
1678 {
1679 xf_ResizeDesktopWindow(xfc, xfc->window, sessionWidth, sessionHeight);
1680 }
1681
1682 xf_draw_screen(xfc, 0, 0, sessionWidth, sessionHeight);
1683 return TRUE;
1684}
1685
1686 case XK_1: /* Ctrl-Alt-1: Zoom in */
1687 zdx = zdy = 10;
1688 break;
1689
1690 case XK_2: /* Ctrl-Alt-2: Zoom out */
1691 zdx = zdy = -10;
1692 break;
1693
1694 case XK_3: /* Ctrl-Alt-3: Pan left */
1695 pdx = -10;
1696 break;
1697
1698 case XK_4: /* Ctrl-Alt-4: Pan right */
1699 pdx = 10;
1700 break;
1701
1702 case XK_5: /* Ctrl-Alt-5: Pan up */
1703 pdy = -10;
1704 break;
1705
1706 case XK_6: /* Ctrl-Alt-6: Pan up */
1707 pdy = 10;
1708 break;
1709 default:
1710 break;
1711 }
1712
1713 if (pdx != 0 || pdy != 0)
1714 {
1715 PanningChangeEventArgs e;
1716 EventArgsInit(&e, "xfreerdp");
1717 e.dx = pdx;
1718 e.dy = pdy;
1719 if (PubSub_OnPanningChange(ctx->pubSub, xfc, &e)<0)
1720 return FALSE;
1721 return TRUE;
1722 }
1723
1724 if (zdx != 0 || zdy != 0)
1725 {
1726 ZoomingChangeEventArgs e;
1727 EventArgsInit(&e, "xfreerdp");
1728 e.dx = zdx;
1729 e.dy = zdy;
1730 if (PubSub_OnZoomingChange(ctx->pubSub, xfc, &e)<0)
1731 return FALSE;
1732 return TRUE;
1733 }
1734 }
1735 }
1736
1737#endif /* WITH_XRENDER defined */
1738#endif /* pinch/zoom/pan simulation */
1739 return FALSE;
1740}
1741
1742void xf_keyboard_handle_special_keys_release(xfContext* xfc, KeySym keysym)
1743{
1744 if (keysym != XK_Control_R)
1745 return;
1746
1747 xfc->wasRightCtrlAlreadyPressed = FALSE;
1748
1749 if (!xfc->ungrabKeyboardWithRightCtrl)
1750 return;
1751
1752 // all requirements for ungrab are fulfilled, ungrabbing now
1753 const UINT32 mods = xk_keyboard_get_modifier_keys(xfc);
1754
1755 if (((mods & (FREERDP_CLIENT_KBD_MOD_RCONTROL)) == 0))
1756 {
1757 if (!xfc->fullscreen)
1758 {
1759 xf_sync_kbd_state(xfc);
1760 freerdp_client_encomsp_toggle_control(xfc->common.encomsp);
1761 }
1762
1763 xfc->mouse_active = FALSE;
1764 xf_ungrab(xfc);
1765 }
1766
1767 // ungrabbed
1768 xfc->ungrabKeyboardWithRightCtrl = FALSE;
1769}
1770
1771BOOL xf_keyboard_set_indicators(rdpContext* context, UINT16 led_flags)
1772{
1773 xfContext* xfc = (xfContext*)context;
1774 xf_keyboard_set_key_state(xfc, led_flags & KBD_SYNC_SCROLL_LOCK, XK_Scroll_Lock);
1775 xf_keyboard_set_key_state(xfc, led_flags & KBD_SYNC_NUM_LOCK, XK_Num_Lock);
1776 xf_keyboard_set_key_state(xfc, led_flags & KBD_SYNC_CAPS_LOCK, XK_Caps_Lock);
1777 xf_keyboard_set_key_state(xfc, led_flags & KBD_SYNC_KANA_LOCK, XK_Kana_Lock);
1778 return TRUE;
1779}
1780
1781BOOL xf_keyboard_set_ime_status(rdpContext* context, UINT16 imeId, UINT32 imeState,
1782 UINT32 imeConvMode)
1783{
1784 if (!context)
1785 return FALSE;
1786
1787 WLog_WARN(TAG,
1788 "KeyboardSetImeStatus(unitId=%04" PRIx16 ", imeState=%08" PRIx32
1789 ", imeConvMode=%08" PRIx32 ") ignored",
1790 imeId, imeState, imeConvMode);
1791 return TRUE;
1792}
1793
1794BOOL xf_ungrab(xfContext* xfc)
1795{
1796 WINPR_ASSERT(xfc);
1797 if (xfc->deferAlt)
1798 xf_keyboard_release_all_keypress(xfc);
1799 XUngrabKeyboard(xfc->display, CurrentTime);
1800 XUngrabPointer(xfc->display, CurrentTime);
1801 xfc->common.mouse_grabbed = FALSE;
1802 return TRUE;
1803}
WINPR_ATTR_NODISCARD FREERDP_API BOOL freerdp_settings_set_uint32(rdpSettings *settings, FreeRDP_Settings_Keys_UInt32 id, UINT32 val)
Sets a UINT32 settings value.
WINPR_ATTR_NODISCARD FREERDP_API UINT32 freerdp_settings_get_uint32(const rdpSettings *settings, FreeRDP_Settings_Keys_UInt32 id)
Returns a UINT32 settings value.
WINPR_ATTR_NODISCARD FREERDP_API BOOL freerdp_settings_get_bool(const rdpSettings *settings, FreeRDP_Settings_Keys_Bool id)
Returns a boolean settings value.
This struct contains function pointer to initialize/free objects.
Definition collections.h:52
OBJECT_FREE_FN fnObjectFree
Definition collections.h:59
WINPR_ATTR_NODISCARD OBJECT_NEW_FN fnObjectNew
Definition collections.h:54