FreeRDP
Loading...
Searching...
No Matches
TestX11DeferredAlt.c
1/* FreeRDP: Licensed under the Apache License, Version 2.0. */
2#include <freerdp/config.h>
3#include <X11/keysym.h>
4#include <X11/XKBlib.h>
5#include <X11/Xutil.h>
6#include <freerdp/input.h>
7#include <freerdp/client.h>
8#include <freerdp/locale/locale.h>
9#include "../xf_keyboard.h"
10#include "../xf_event.h"
11
12static const Display* cdisplay_stub = (const Display*)0x123456789abcdef;
13static Display* display_stub = (Display*)0x123456789abcdef;
14
15/* Run the real X11 keyboard/focus/button handlers with a deterministic X server
16 * and input sink. No display, window manager, RDP server or sleeps are needed. */
17static char trace[1024];
18static size_t used;
19static BOOL failAlt;
20static BOOL minimized;
21static unsigned ungrabs;
22static BOOL altGr;
23static char physical[32];
24static XkbDescRec keyboard_map;
25static XkbNamesRec keyboard_names;
26static XkbKeyNameRec keyboard_key_names[256];
27static XModifierKeymap modifier_map;
28static KeySym keyboard_keysym;
29
30struct keys_t
31{
32 KeySym sym;
33 KeyCode code;
34 DWORD scan;
35 char name;
36};
37static const struct keys_t keys[] = { { XK_Alt_L, 64, RDP_SCANCODE_LMENU, 'A' },
38 { XK_Alt_R, 108, RDP_SCANCODE_RMENU, 'R' },
39 { XK_Shift_L, 50, RDP_SCANCODE_LSHIFT, 'S' },
40 { XK_Shift_R, 62, RDP_SCANCODE_RSHIFT, 'H' },
41 { XK_Control_L, 37, RDP_SCANCODE_LCONTROL, 'C' },
42 { XK_Control_R, 105, RDP_SCANCODE_RCONTROL, 'D' },
43 { XK_Super_L, 133, RDP_SCANCODE_LWIN, 'L' },
44 { XK_Super_R, 134, RDP_SCANCODE_RWIN, 'G' },
45 { XK_f, 41, RDP_SCANCODE_KEY_F, 'F' },
46 { XK_Tab, 23, RDP_SCANCODE_TAB, 'T' },
47 { XK_m, 58, RDP_SCANCODE_KEY_M, 'N' } };
48
49static void record(char name, int direction)
50{
51 WINPR_ASSERT(used + 2 < sizeof(trace));
52 trace[used++] = name;
53 trace[used++] = WINPR_CXX_COMPAT_CAST(char, (direction & 0xff));
54 trace[used] = '\0';
55}
56
57Bool XGetEventData(Display* display, XGenericEventCookie* cookie)
58{
59 WINPR_UNUSED(display);
60 WINPR_ASSERT(display == cdisplay_stub);
61 memset(cookie, 0, sizeof(XGenericEventCookie));
62 return True;
63}
64
65void XFreeEventData(Display* display, WINPR_ATTR_UNUSED XGenericEventCookie* cookie)
66{
67 WINPR_UNUSED(display);
68 WINPR_ASSERT(display == cdisplay_stub);
69}
70
71KeyCode XKeysymToKeycode(Display* display, KeySym sym)
72{
73 WINPR_UNUSED(display);
74 WINPR_ASSERT(display == cdisplay_stub);
75 if (sym == XK_ISO_Level3_Shift || sym == XK_Mode_switch)
76 return altGr ? 108 : 0;
77 for (size_t i = 0; i < ARRAYSIZE(keys); i++)
78 {
79 if (keys[i].sym == sym)
80 return keys[i].code;
81 }
82 return 0;
83}
84
85XkbDescPtr XkbGetMap(Display* display, unsigned int which, unsigned int device_spec)
86{
87 WINPR_UNUSED(display);
88 WINPR_ASSERT(display == cdisplay_stub);
89 WINPR_UNUSED(which);
90 WINPR_UNUSED(device_spec);
91 return &keyboard_map;
92}
93
94Status XkbGetNames(Display* display, unsigned int which, XkbDescPtr desc)
95{
96 WINPR_UNUSED(display);
97 WINPR_ASSERT(display == cdisplay_stub);
98 WINPR_UNUSED(which);
99 WINPR_UNUSED(desc);
100 return Success;
101}
102
103void XkbFreeKeyboard(XkbDescPtr xkb, unsigned int which, Bool free_desc)
104{
105 WINPR_UNUSED(xkb);
106 WINPR_UNUSED(which);
107 WINPR_UNUSED(free_desc);
108}
109
110KeySym XkbKeycodeToKeysym(Display* display,
111#if NeedWidePrototypes
112 unsigned int
113#else
114 KeyCode
115#endif
116 keycode,
117 int group, int level)
118{
119 WINPR_UNUSED(display);
120 WINPR_ASSERT(display == cdisplay_stub);
121 WINPR_UNUSED(keycode);
122 WINPR_UNUSED(group);
123 WINPR_UNUSED(level);
124 return keyboard_keysym;
125}
126
127int XDisplayKeycodes(Display* display, int* min_keycodes_return, int* max_keycodes_return)
128{
129 WINPR_UNUSED(display);
130 WINPR_ASSERT(display == cdisplay_stub);
131 *min_keycodes_return = keyboard_map.min_key_code;
132 *max_keycodes_return = keyboard_map.max_key_code;
133 return 1;
134}
135
136XModifierKeymap* XGetModifierMapping(Display* display)
137{
138 WINPR_UNUSED(display);
139 WINPR_ASSERT(display == cdisplay_stub);
140 return &modifier_map;
141}
142
143int XFreeModifiermap(XModifierKeymap* modmap)
144{
145 WINPR_UNUSED(modmap);
146 return 1;
147}
148
149int XQueryKeymap(Display* display, char map[32])
150{
151 WINPR_UNUSED(display);
152 WINPR_ASSERT(display == cdisplay_stub);
153 memcpy(map, physical, sizeof(physical));
154 return 1;
155}
156
157Bool XQueryPointer(Display* display, Window window, Window* root, Window* child, int* rx, int* ry,
158 int* x, int* y, unsigned int* mask)
159{
160 WINPR_UNUSED(display);
161 WINPR_ASSERT(display == cdisplay_stub);
162 WINPR_UNUSED(window);
163 *root = *child = None;
164 *rx = *ry = *x = *y = 0;
165 *mask = 0;
166 return False;
167}
168
169int XSync(Display* display, Bool discard)
170{
171 WINPR_UNUSED(display);
172 WINPR_ASSERT(display == cdisplay_stub);
173 WINPR_UNUSED(discard);
174 return 1;
175}
176
177int XIconifyWindow(Display* display, Window window, int screen)
178{
179 WINPR_UNUSED(display);
180 WINPR_ASSERT(display == cdisplay_stub);
181 WINPR_UNUSED(window);
182 WINPR_UNUSED(screen);
183 minimized = TRUE;
184 return 1;
185}
186
187int XUngrabKeyboard(Display* display, Time time)
188{
189 WINPR_UNUSED(display);
190 WINPR_ASSERT(display == cdisplay_stub);
191 WINPR_UNUSED(time);
192 ungrabs++;
193 return 1;
194}
195
196int XUngrabPointer(Display* display, Time time)
197{
198 WINPR_UNUSED(display);
199 WINPR_ASSERT(display == cdisplay_stub);
200 WINPR_UNUSED(time);
201 return 1;
202}
203
204BOOL freerdp_input_send_keyboard_event_ex(rdpInput* input, BOOL down, BOOL repeat,
205 UINT32 rdp_scancode)
206{
207 WINPR_UNUSED(input);
208 if (failAlt && down &&
209 ((rdp_scancode == RDP_SCANCODE_LMENU) || (rdp_scancode == RDP_SCANCODE_RMENU)))
210 return FALSE;
211 for (size_t i = 0; i < ARRAYSIZE(keys); i++)
212 {
213 const struct keys_t* cur = &keys[i];
214 if (cur->scan == rdp_scancode)
215 {
216
217 record(cur->name, down ? (repeat ? '*' : '+') : '-');
218 return TRUE;
219 }
220 }
221 record('?', down ? '+' : '-');
222 return TRUE;
223}
224
225BOOL freerdp_input_send_synchronize_event(rdpInput* input, UINT32 flags)
226{
227 WINPR_UNUSED(input);
228 WINPR_UNUSED(flags);
229 return TRUE;
230}
231
232BOOL freerdp_input_send_focus_in_event(rdpInput* input, UINT16 toggleStates)
233{
234 WINPR_UNUSED(input);
235 WINPR_UNUSED(toggleStates);
236 return TRUE;
237}
238
239BOOL freerdp_client_send_button_event(rdpClientContext* cctx, BOOL relative, UINT16 mflags, INT32 x,
240 INT32 y)
241{
242 WINPR_UNUSED(cctx);
243 WINPR_UNUSED(relative);
244 WINPR_UNUSED(x);
245 WINPR_UNUSED(y);
246 record((mflags & PTR_FLAGS_MOVE) ? 'M' : 'B', (mflags & PTR_FLAGS_DOWN) ? '+' : '-');
247 return TRUE;
248}
249
250BOOL freerdp_client_send_extended_button_event(rdpClientContext* cctx, BOOL relative, UINT16 mflags,
251 INT32 x, INT32 y)
252{
253 WINPR_UNUSED(cctx);
254 WINPR_UNUSED(relative);
255 WINPR_UNUSED(x);
256 WINPR_UNUSED(y);
257 record('E', (mflags & PTR_XFLAGS_DOWN) ? '+' : '-');
258 return TRUE;
259}
260
261BOOL freerdp_client_send_wheel_event(rdpClientContext* cctx, UINT16 flags)
262{
263 WINPR_UNUSED(cctx);
264 WINPR_UNUSED(flags);
265 record('W', '+');
266 return TRUE;
267}
268
269static void key(xfContext* xfc, KeySym sym, BOOL down)
270{
271 XKeyEvent event = WINPR_C_ARRAY_INIT;
272 event.keycode = XKeysymToKeycode(xfc->display, sym);
273 if (down)
274 {
275 const int val = physical[event.keycode / 8] | (1 << (event.keycode % 8));
276 physical[event.keycode / 8] = WINPR_CXX_COMPAT_CAST(char, (val & 0xff));
277 xf_keyboard_key_press(xfc, &event, sym);
278 }
279 else
280 {
281 const int val = physical[event.keycode / 8] & ~(1 << (event.keycode % 8));
282 physical[event.keycode / 8] = WINPR_CXX_COMPAT_CAST(char, (val & 0xff));
283 xf_keyboard_key_release(xfc, &event, sym);
284 }
285}
286
287WINPR_ATTR_NODISCARD
288static BOOL focus(xfContext* xfc, int type, int mode)
289{
290 XEvent event = WINPR_C_ARRAY_INIT;
291 event.xfocus.type = type;
292 event.xfocus.mode = mode;
293 return xf_event_process(xfc->common.context.instance, &event);
294}
295
296typedef enum
297{
298 END,
299 ALT_DOWN,
300 ALT_UP,
301 SHIFT_DOWN,
302 SHIFT_UP,
303 CTRL_DOWN,
304 CTRL_UP,
305 RIGHT_ALT_DOWN,
306 RIGHT_ALT_UP,
307 F_DOWN,
308 F_UP,
309 TAB_UP,
310 RIGHT_SHIFT_DOWN,
311 RIGHT_SHIFT_UP,
312 M_DOWN,
313 M_UP,
314 RIGHT_CTRL_DOWN,
315 RIGHT_CTRL_UP,
316 TAB_DOWN,
317 GRAB_OUT,
318 GRAB_IN,
319 FOCUS_OUT,
320 FOCUS_IN,
321 WHILE_GRABBED_OUT,
322 MOTION,
323 BUTTON_DOWN,
324 BUTTON_UP,
325 WHEEL_DOWN,
326 WHEEL_UP,
327 RAW_BUTTON_DOWN,
328 LOCAL_CANCEL
329} Event;
330
331static const struct
332{
333 KeySym sym;
334 BOOL down;
335} keyEvents[] = {
336 { 0, FALSE }, { XK_Alt_L, TRUE }, { XK_Alt_L, FALSE }, { XK_Shift_L, TRUE },
337 { XK_Shift_L, FALSE }, { XK_Control_L, TRUE }, { XK_Control_L, FALSE }, { XK_Alt_R, TRUE },
338 { XK_Alt_R, FALSE }, { XK_f, TRUE }, { XK_f, FALSE }, { XK_Tab, FALSE },
339 { XK_Shift_R, TRUE }, { XK_Shift_R, FALSE }, { XK_m, TRUE }, { XK_m, FALSE },
340 { XK_Control_R, TRUE }, { XK_Control_R, FALSE }, { XK_Tab, TRUE }
341};
342
343enum
344{
345 DEFAULT_MODE = 1,
346 UNICODE_MODE = 2,
347 FAIL_ALT = 4,
348 ALTGR_MODE = 8,
349 EXPECT_MINIMIZED = 32,
350 GRAB_KEYBOARD = 64,
351 EXPECT_UNGRAB = 128
352};
353static const struct
354{
355 const char* name;
356 Event events[16];
357 const char* expected;
358 unsigned flags;
359} cases[] = {
360 { "right Ctrl ungrab forwards release and subsequent input",
361 { RIGHT_CTRL_DOWN, RIGHT_CTRL_UP, F_DOWN, F_UP },
362 "D+D-F+F-",
363 EXPECT_UNGRAB },
364 { "right Ctrl ungrab with keyboard grabbing enabled",
365 { RIGHT_CTRL_DOWN, RIGHT_CTRL_UP, F_DOWN, F_UP },
366 "D+D-F+F-",
367 EXPECT_UNGRAB | GRAB_KEYBOARD },
368 { "default right Ctrl ungrab",
369 { RIGHT_CTRL_DOWN, RIGHT_CTRL_UP, F_DOWN, F_UP },
370 "D+D-F+F-",
371 EXPECT_UNGRAB | DEFAULT_MODE },
372 { "remote Alt+Tab", { ALT_DOWN, TAB_DOWN, TAB_UP, ALT_UP }, "A+T+T-A-", GRAB_KEYBOARD },
373 { "default sends immediately", { ALT_DOWN }, "A+", DEFAULT_MODE },
374 { "default Alt tap", { ALT_DOWN, ALT_UP }, "A+A-", DEFAULT_MODE },
375 { "pending repeats", { ALT_DOWN, ALT_DOWN }, "", 0 },
376 { "bare tap", { ALT_DOWN, ALT_DOWN, ALT_UP }, "A+A-", 0 },
377 { "Unicode Alt tap uses scancodes", { ALT_DOWN, ALT_UP }, "A+A-", UNICODE_MODE },
378 { "Alt+F ordering and repeat", { ALT_DOWN, F_DOWN, F_DOWN, F_UP, ALT_UP }, "A+F+F*F-A-", 0 },
379 { "Shift alone remains immediate", { SHIFT_DOWN, SHIFT_UP }, "S+S-", 0 },
380 { "Shift before Alt",
381 { SHIFT_DOWN, ALT_DOWN, F_DOWN, F_UP, ALT_UP, SHIFT_UP },
382 "S+A+F+F-A-S-",
383 0 },
384 { "Shift does not flush Alt", { ALT_DOWN, SHIFT_DOWN }, "S+", 0 },
385 { "Shift repeats do not flush Alt", { ALT_DOWN, SHIFT_DOWN, SHIFT_DOWN }, "S+S*", 0 },
386 { "Alt before Shift",
387 { ALT_DOWN, SHIFT_DOWN, F_DOWN, F_UP, SHIFT_UP, ALT_UP },
388 "S+A+F+F-S-A-",
389 0 },
390 { "Alt first, Shift release commits chord",
391 { ALT_DOWN, SHIFT_DOWN, SHIFT_UP, ALT_UP },
392 "S+A+S-A-",
393 0 },
394 { "Shift first, Alt release commits tap",
395 { SHIFT_DOWN, ALT_DOWN, ALT_UP, SHIFT_UP },
396 "S+A+A-S-",
397 0 },
398 { "Shift first, Shift release commits chord",
399 { SHIFT_DOWN, ALT_DOWN, SHIFT_UP, ALT_UP },
400 "S+A+S-A-",
401 0 },
402 { "right Shift release commits chord",
403 { RIGHT_ALT_DOWN, RIGHT_SHIFT_DOWN, RIGHT_SHIFT_UP, RIGHT_ALT_UP },
404 "H+R+H-R-",
405 0 },
406 { "other modifier flushes Alt", { ALT_DOWN, CTRL_DOWN, CTRL_UP, ALT_UP }, "A+C+C-A-", 0 },
407 { "right Alt default immediate", { RIGHT_ALT_DOWN }, "R+", DEFAULT_MODE },
408 { "right Alt pending repeats", { RIGHT_ALT_DOWN, RIGHT_ALT_DOWN }, "", 0 },
409 { "right Alt tap", { RIGHT_ALT_DOWN, RIGHT_ALT_UP }, "R+R-", 0 },
410 { "right Alt Unicode tap", { RIGHT_ALT_DOWN, RIGHT_ALT_UP }, "R+R-", UNICODE_MODE },
411 { "AltGr immediate", { RIGHT_ALT_DOWN }, "R+", ALTGR_MODE },
412 { "AltGr chord", { RIGHT_ALT_DOWN, F_DOWN, F_UP, RIGHT_ALT_UP }, "R+F+F-R-", ALTGR_MODE },
413 { "both Alt keys remain pending", { ALT_DOWN, RIGHT_ALT_DOWN }, "", 0 },
414 { "both Alt keys flush left then right",
415 { RIGHT_ALT_DOWN, ALT_DOWN, ALT_UP, RIGHT_ALT_UP },
416 "A+R+A-R-",
417 0 },
418 { "both Alt keys canceled",
419 { ALT_DOWN, RIGHT_ALT_DOWN, GRAB_OUT, GRAB_IN, ALT_UP, RIGHT_ALT_UP },
420 "",
421 0 },
422 { "host consumes Tab without changing selection",
423 { ALT_DOWN, GRAB_OUT, ALT_UP, GRAB_IN },
424 "",
425 0 },
426 { "host Alt+Shift+Tab",
427 { ALT_DOWN, SHIFT_DOWN, GRAB_OUT, FOCUS_OUT, SHIFT_UP, ALT_UP, FOCUS_IN, TAB_UP },
428 "S+S-",
429 0 },
430 { "host Shift+Alt+Tab",
431 { SHIFT_DOWN, ALT_DOWN, GRAB_OUT, SHIFT_UP, ALT_UP, GRAB_IN },
432 "S+S-",
433 0 },
434 { "right Alt+right Shift host switch",
435 { RIGHT_ALT_DOWN, RIGHT_SHIFT_DOWN, GRAB_OUT, RIGHT_SHIFT_UP, RIGHT_ALT_UP, GRAB_IN },
436 "H+H-",
437 0 },
438 { "pending release after return is ignored", { ALT_DOWN, FOCUS_OUT, FOCUS_IN, ALT_UP }, "", 0 },
439 { "held Alt is not restored for guest input",
440 { ALT_DOWN, GRAB_OUT, GRAB_IN, F_DOWN, F_UP, ALT_UP },
441 "F+F-",
442 0 },
443 { "cleanup releases sent keys", { ALT_DOWN, F_DOWN, WHILE_GRABBED_OUT }, "A+F+F-A-", 0 },
444 { "cleanup does not repeat releases",
445 { ALT_DOWN, F_DOWN, WHILE_GRABBED_OUT, F_UP, ALT_UP, FOCUS_IN },
446 "A+F+F-A-",
447 0 },
448 { "pointer motion leaves Alt pending", { ALT_DOWN, MOTION }, "M-", 0 },
449 { "Alt+button", { ALT_DOWN, MOTION, BUTTON_DOWN, BUTTON_UP, ALT_UP }, "M-A+B+B-A-", 0 },
450 { "right Alt+button", { RIGHT_ALT_DOWN, BUTTON_DOWN, BUTTON_UP, RIGHT_ALT_UP }, "R+B+B-R-", 0 },
451 { "wheel release does not flush", { ALT_DOWN, WHEEL_UP }, "", 0 },
452 { "Alt+wheel", { ALT_DOWN, WHEEL_UP, WHEEL_DOWN, ALT_UP }, "A+W+A-", 0 },
453 { "Alt+raw extended button", { ALT_DOWN, RAW_BUTTON_DOWN, ALT_UP }, "A+E+A-", 0 },
454 { "local action cancels unused Alt", { ALT_DOWN, LOCAL_CANCEL, ALT_UP }, "", 0 },
455 { "local action retains sent Alt release",
456 { ALT_DOWN, F_DOWN, F_UP, LOCAL_CANCEL, ALT_UP },
457 "A+F+F-A-",
458 0 },
459 { "failed Alt-down produces no Alt-up", { ALT_DOWN, ALT_UP }, "", FAIL_ALT },
460 { "failed right Alt-down produces no Alt-up", { RIGHT_ALT_DOWN, RIGHT_ALT_UP }, "", FAIL_ALT },
461 { "failed Alt-down blocks guest key", { ALT_DOWN, F_DOWN, F_UP, ALT_UP }, "", FAIL_ALT },
462 { "canceled Alt release leaves other Alt pending",
463 { ALT_DOWN, GRAB_OUT, GRAB_IN, RIGHT_ALT_DOWN, ALT_UP, RIGHT_ALT_UP },
464 "R+R-",
465 0 },
466 { "local shortcut sees pending physical Alt",
467 { CTRL_DOWN, ALT_DOWN, M_DOWN, M_UP, ALT_UP, CTRL_UP },
468 "C+C-",
469 EXPECT_MINIMIZED }
470};
471
472WINPR_ATTR_NODISCARD
473static BOOL dispatch(xfContext* xfc, Event event)
474{
475 if (event > END && event < ARRAYSIZE(keyEvents))
476 {
477 key(xfc, keyEvents[event].sym, keyEvents[event].down);
478 return TRUE;
479 }
480 switch (event)
481 {
482 case GRAB_OUT:
483 return focus(xfc, FocusOut, NotifyGrab);
484 case GRAB_IN:
485 return focus(xfc, FocusIn, NotifyUngrab);
486 case FOCUS_OUT:
487 return focus(xfc, FocusOut, NotifyNormal);
488 case FOCUS_IN:
489 return focus(xfc, FocusIn, NotifyNormal);
490 case WHILE_GRABBED_OUT:
491 return focus(xfc, FocusOut, NotifyWhileGrabbed);
492 case MOTION:
493 return xf_generic_MotionNotify(xfc, 10, 20, None, FALSE);
494 case BUTTON_DOWN:
495 case BUTTON_UP:
496 return xf_generic_ButtonEvent(xfc, 10, 20, 1, None, FALSE, event == BUTTON_DOWN);
497 case WHEEL_DOWN:
498 case WHEEL_UP:
499 return xf_generic_ButtonEvent(xfc, 10, 20, 4, None, FALSE, event == WHEEL_DOWN);
500 case RAW_BUTTON_DOWN:
501 return xf_generic_RawButtonEvent(xfc, 8, FALSE, TRUE);
502 case LOCAL_CANCEL:
503 xfc->deferredAlt.pending = 0;
504 return TRUE;
505 default:
506 return FALSE;
507 }
508}
509
510WINPR_ATTR_NODISCARD
511static BOOL test_keyboard_mapping_reload(void)
512{
513 BOOL rc = FALSE;
514 xfContext xfc = WINPR_C_ARRAY_INIT;
515 rdpSettings* settings = freerdp_settings_new(0);
516 if (!settings)
517 return FALSE;
518
519 keyboard_map = (XkbDescRec)WINPR_C_ARRAY_INIT;
520 keyboard_names = (XkbNamesRec)WINPR_C_ARRAY_INIT;
521 memset(keyboard_key_names, 0, sizeof(keyboard_key_names));
522 keyboard_map.min_key_code = 38;
523 keyboard_map.max_key_code = 39;
524 keyboard_map.names = &keyboard_names;
525 keyboard_names.keys = keyboard_key_names;
526 keyboard_keysym = XK_a;
527 xfc.common.context.settings = settings;
528 xfc.display = display_stub;
529 xfc.log = WLog_Get("com.freerdp.client.x11.test.keyboard");
530 if (!freerdp_settings_set_uint32(settings, FreeRDP_KeyboardLayout, ENGLISH_UNITED_STATES))
531 {
532 (void)fprintf(stderr, "%s: 1: KeyboardLayout could not be set\n", __func__);
533 goto out;
534 }
535
536 memcpy(keyboard_key_names[38].name, "AC01", XkbKeyNameLength);
537 if (!xf_keyboard_init(&xfc) || (xfc.X11_KEYCODE_TO_VIRTUAL_SCANCODE[38] != RDP_SCANCODE_KEY_A))
538 {
539 (void)fprintf(stderr, "%s: 2: xf_keyboard_init failed or %u != %u\n", __func__,
540 xfc.X11_KEYCODE_TO_VIRTUAL_SCANCODE[38], RDP_SCANCODE_KEY_A);
541 goto out;
542 }
543
544#if defined(__APPLE__)
545 const DWORD expect = RDP_SCANCODE_OEM_6;
546#else
547 const DWORD expect = RDP_SCANCODE_KEY_A;
548#endif
549
550 /* A new map with non-standard key names must be recovered through keysyms. */
551 memset(keyboard_key_names[38].name, 0, XkbKeyNameLength);
552 memcpy(keyboard_key_names[38].name, "L001", XkbKeyNameLength);
553 if (!xf_keyboard_init(&xfc) || (xfc.X11_KEYCODE_TO_VIRTUAL_SCANCODE[38] != expect))
554 {
555 (void)fprintf(stderr, "%s: 3: xf_keyboard_init failed or %u != %u\n", __func__,
556 xfc.X11_KEYCODE_TO_VIRTUAL_SCANCODE[38], expect);
557 goto out;
558 }
559
560 /* If the replacement map is unusable, retain the last working mapping. */
561 keyboard_keysym = NoSymbol;
562 if (!xf_keyboard_init(&xfc) || (xfc.X11_KEYCODE_TO_VIRTUAL_SCANCODE[38] != expect))
563 {
564 (void)fprintf(stderr, "%s: 4: xf_keyboard_init failed or %u != %u\n", __func__,
565 xfc.X11_KEYCODE_TO_VIRTUAL_SCANCODE[38], expect);
566 goto out;
567 }
568
569 rc = TRUE;
570out:
571 xf_keyboard_free(&xfc);
572 freerdp_settings_free(settings);
573 return rc;
574}
575
576int main(int argc, char* argv[])
577{
578 WINPR_UNUSED(argc);
579 WINPR_UNUSED(argv);
580 if (!test_keyboard_mapping_reload())
581 return 1;
582
583 int rc = 1;
584 xfContext xfc = WINPR_C_ARRAY_INIT;
585 xfWindow window = WINPR_C_ARRAY_INIT;
586 XModifierKeymap modifiers = WINPR_C_ARRAY_INIT;
587 freerdp instance = WINPR_C_ARRAY_INIT;
588 rdpInput input = WINPR_C_ARRAY_INIT;
589 rdpSettings* settings = freerdp_settings_new(0);
590 /* xf_minimize() publishes a window state change */
591 wPubSub* pubSub = PubSub_New(TRUE);
592 if (!settings || !pubSub)
593 {
594 (void)fprintf(stderr, "settings=%p, pubSub=%p\n", (const void*)settings,
595 (const void*)pubSub);
596 goto out;
597 }
598 xfc.common.context.settings = settings;
599 xfc.common.context.pubSub = pubSub;
600 xfc.common.context.input = &input;
601 xfc.common.context.instance = &instance;
602 instance.context = &xfc.common.context;
603 xfc.window = &window;
604 xfc.display = display_stub; /* All X calls used in this test are mocked. */
605 xfc.modifierMap = &modifiers;
606 xfc.log = WLog_Get("com.freerdp.client.x11.test.deferred-alt");
607 for (size_t i = 0; i < ARRAYSIZE(keys); i++)
608 xfc.X11_KEYCODE_TO_VIRTUAL_SCANCODE[keys[i].code] = keys[i].scan;
609 xfc.button_map[0].button = 1;
610 xfc.button_map[0].flags = PTR_FLAGS_BUTTON1;
611 xfc.button_map[1].button = 4;
612 xfc.button_map[1].flags = PTR_FLAGS_WHEEL | 120;
613 xfc.button_map[2].button = 8;
614 xfc.button_map[2].flags = PTR_XFLAGS_BUTTON1;
615 for (size_t i = 0; i < ARRAYSIZE(cases); i++)
616 {
617 memset(xfc.KeyboardState, 0, sizeof(xfc.KeyboardState));
618 memset(&xfc.deferredAlt, 0, sizeof(xfc.deferredAlt));
619 memset(physical, 0, sizeof(physical));
620 used = 0;
621 trace[0] = '\0';
622 failAlt = (cases[i].flags & FAIL_ALT) != 0;
623 minimized = FALSE;
624 ungrabs = 0;
625 xfc.wasRightCtrlAlreadyPressed = FALSE;
626 xfc.ungrabKeyboardWithRightCtrl = FALSE;
627 xfc.grab_keyboard = (cases[i].flags & GRAB_KEYBOARD) != 0;
628 altGr = (cases[i].flags & ALTGR_MODE) != 0;
629 xfc.deferAlt = !(cases[i].flags & DEFAULT_MODE);
630 if (!freerdp_settings_set_bool(settings, FreeRDP_GrabKeyboard, xfc.grab_keyboard) ||
631 !freerdp_settings_set_bool(settings, FreeRDP_UnicodeInput,
632 (cases[i].flags & UNICODE_MODE) != 0))
633 {
634 (void)fprintf(stderr, "%s: GrabKeyboard or UnicodeInput or %" PRIu32 " != 0\n",
635 cases[i].name, cases[i].flags & UNICODE_MODE);
636 goto out;
637 }
638 for (size_t j = 0; j < ARRAYSIZE(cases[i].events) && cases[i].events[j] != END; j++)
639 {
640 if (!dispatch(&xfc, cases[i].events[j]))
641 {
642 (void)fprintf(stderr, "%s: event %zu failed\n", cases[i].name, j);
643 goto out;
644 }
645 }
646 if ((cases[i].flags & EXPECT_UNGRAB) && (ungrabs != 1))
647 {
648 (void)fprintf(stderr, "%s: expected one keyboard ungrab, got %u\n", cases[i].name,
649 ungrabs);
650 goto out;
651 }
652 if ((cases[i].flags & EXPECT_MINIMIZED) && !minimized)
653 {
654 (void)fprintf(stderr, "%s: local shortcut did not minimize\n", cases[i].name);
655 goto out;
656 }
657 if (strcmp(trace, cases[i].expected) != 0)
658 {
659 (void)fprintf(stderr, "%s: expected '%s', got '%s'\n", cases[i].name, cases[i].expected,
660 trace);
661 goto out;
662 }
663 }
664 rc = 0;
665out:
666 PubSub_Free(pubSub);
667 freerdp_settings_free(settings);
668 return rc;
669}
FREERDP_API rdpSettings * freerdp_settings_new(DWORD flags)
creates a new setting struct
WINPR_ATTR_NODISCARD FREERDP_API BOOL freerdp_settings_set_bool(rdpSettings *settings, FreeRDP_Settings_Keys_Bool id, BOOL val)
Sets a BOOL settings value.
WINPR_ATTR_NODISCARD FREERDP_API BOOL freerdp_settings_set_uint32(rdpSettings *settings, FreeRDP_Settings_Keys_UInt32 id, UINT32 val)
Sets a UINT32 settings value.
FREERDP_API void freerdp_settings_free(rdpSettings *settings)
Free a settings struct with all data in it.