2#include <freerdp/config.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"
12static const Display* cdisplay_stub = (
const Display*)0x123456789abcdef;
13static Display* display_stub = (Display*)0x123456789abcdef;
17static char trace[1024];
21static unsigned ungrabs;
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;
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' } };
49static void record(
char name,
int direction)
51 WINPR_ASSERT(used + 2 <
sizeof(trace));
53 trace[used++] = WINPR_CXX_COMPAT_CAST(
char, (direction & 0xff));
57Bool XGetEventData(Display* display, XGenericEventCookie* cookie)
59 WINPR_UNUSED(display);
60 WINPR_ASSERT(display == cdisplay_stub);
61 memset(cookie, 0,
sizeof(XGenericEventCookie));
65void XFreeEventData(Display* display, WINPR_ATTR_UNUSED XGenericEventCookie* cookie)
67 WINPR_UNUSED(display);
68 WINPR_ASSERT(display == cdisplay_stub);
71KeyCode XKeysymToKeycode(Display* display, KeySym sym)
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++)
79 if (keys[i].sym == sym)
85XkbDescPtr XkbGetMap(Display* display,
unsigned int which,
unsigned int device_spec)
87 WINPR_UNUSED(display);
88 WINPR_ASSERT(display == cdisplay_stub);
90 WINPR_UNUSED(device_spec);
94Status XkbGetNames(Display* display,
unsigned int which, XkbDescPtr desc)
96 WINPR_UNUSED(display);
97 WINPR_ASSERT(display == cdisplay_stub);
103void XkbFreeKeyboard(XkbDescPtr xkb,
unsigned int which, Bool free_desc)
107 WINPR_UNUSED(free_desc);
110KeySym XkbKeycodeToKeysym(Display* display,
111#
if NeedWidePrototypes
117 int group,
int level)
119 WINPR_UNUSED(display);
120 WINPR_ASSERT(display == cdisplay_stub);
121 WINPR_UNUSED(keycode);
124 return keyboard_keysym;
127int XDisplayKeycodes(Display* display,
int* min_keycodes_return,
int* max_keycodes_return)
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;
136XModifierKeymap* XGetModifierMapping(Display* display)
138 WINPR_UNUSED(display);
139 WINPR_ASSERT(display == cdisplay_stub);
140 return &modifier_map;
143int XFreeModifiermap(XModifierKeymap* modmap)
145 WINPR_UNUSED(modmap);
149int XQueryKeymap(Display* display,
char map[32])
151 WINPR_UNUSED(display);
152 WINPR_ASSERT(display == cdisplay_stub);
153 memcpy(map, physical,
sizeof(physical));
157Bool XQueryPointer(Display* display, Window window, Window* root, Window* child,
int* rx,
int* ry,
158 int* x,
int* y,
unsigned int* mask)
160 WINPR_UNUSED(display);
161 WINPR_ASSERT(display == cdisplay_stub);
162 WINPR_UNUSED(window);
163 *root = *child = None;
164 *rx = *ry = *x = *y = 0;
169int XSync(Display* display, Bool discard)
171 WINPR_UNUSED(display);
172 WINPR_ASSERT(display == cdisplay_stub);
173 WINPR_UNUSED(discard);
177int XIconifyWindow(Display* display, Window window,
int screen)
179 WINPR_UNUSED(display);
180 WINPR_ASSERT(display == cdisplay_stub);
181 WINPR_UNUSED(window);
182 WINPR_UNUSED(screen);
187int XUngrabKeyboard(Display* display, Time time)
189 WINPR_UNUSED(display);
190 WINPR_ASSERT(display == cdisplay_stub);
196int XUngrabPointer(Display* display, Time time)
198 WINPR_UNUSED(display);
199 WINPR_ASSERT(display == cdisplay_stub);
204BOOL freerdp_input_send_keyboard_event_ex(rdpInput* input, BOOL down, BOOL repeat,
208 if (failAlt && down &&
209 ((rdp_scancode == RDP_SCANCODE_LMENU) || (rdp_scancode == RDP_SCANCODE_RMENU)))
211 for (
size_t i = 0; i < ARRAYSIZE(keys); i++)
213 const struct keys_t* cur = &keys[i];
214 if (cur->scan == rdp_scancode)
217 record(cur->name, down ? (repeat ?
'*' :
'+') :
'-');
221 record(
'?', down ?
'+' :
'-');
225BOOL freerdp_input_send_synchronize_event(rdpInput* input, UINT32 flags)
232BOOL freerdp_input_send_focus_in_event(rdpInput* input, UINT16 toggleStates)
235 WINPR_UNUSED(toggleStates);
239BOOL freerdp_client_send_button_event(rdpClientContext* cctx, BOOL relative, UINT16 mflags, INT32 x,
243 WINPR_UNUSED(relative);
246 record((mflags & PTR_FLAGS_MOVE) ?
'M' :
'B', (mflags & PTR_FLAGS_DOWN) ?
'+' :
'-');
250BOOL freerdp_client_send_extended_button_event(rdpClientContext* cctx, BOOL relative, UINT16 mflags,
254 WINPR_UNUSED(relative);
257 record(
'E', (mflags & PTR_XFLAGS_DOWN) ?
'+' :
'-');
261BOOL freerdp_client_send_wheel_event(rdpClientContext* cctx, UINT16 flags)
269static void key(xfContext* xfc, KeySym sym, BOOL down)
271 XKeyEvent
event = WINPR_C_ARRAY_INIT;
272 event.keycode = XKeysymToKeycode(xfc->display, sym);
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);
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);
288static BOOL focus(xfContext* xfc,
int type,
int mode)
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);
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 }
349 EXPECT_MINIMIZED = 32,
357 const char* expected;
360 {
"right Ctrl ungrab forwards release and subsequent input",
361 { RIGHT_CTRL_DOWN, RIGHT_CTRL_UP, F_DOWN, F_UP },
364 {
"right Ctrl ungrab with keyboard grabbing enabled",
365 { RIGHT_CTRL_DOWN, RIGHT_CTRL_UP, F_DOWN, F_UP },
367 EXPECT_UNGRAB | GRAB_KEYBOARD },
368 {
"default right Ctrl ungrab",
369 { RIGHT_CTRL_DOWN, RIGHT_CTRL_UP, F_DOWN, F_UP },
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 },
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 },
390 {
"Alt first, Shift release commits chord",
391 { ALT_DOWN, SHIFT_DOWN, SHIFT_UP, ALT_UP },
394 {
"Shift first, Alt release commits tap",
395 { SHIFT_DOWN, ALT_DOWN, ALT_UP, SHIFT_UP },
398 {
"Shift first, Shift release commits chord",
399 { SHIFT_DOWN, ALT_DOWN, SHIFT_UP, ALT_UP },
402 {
"right Shift release commits chord",
403 { RIGHT_ALT_DOWN, RIGHT_SHIFT_DOWN, RIGHT_SHIFT_UP, RIGHT_ALT_UP },
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 },
418 {
"both Alt keys canceled",
419 { ALT_DOWN, RIGHT_ALT_DOWN, GRAB_OUT, GRAB_IN, ALT_UP, RIGHT_ALT_UP },
422 {
"host consumes Tab without changing selection",
423 { ALT_DOWN, GRAB_OUT, ALT_UP, GRAB_IN },
426 {
"host Alt+Shift+Tab",
427 { ALT_DOWN, SHIFT_DOWN, GRAB_OUT, FOCUS_OUT, SHIFT_UP, ALT_UP, FOCUS_IN, TAB_UP },
430 {
"host Shift+Alt+Tab",
431 { SHIFT_DOWN, ALT_DOWN, GRAB_OUT, SHIFT_UP, ALT_UP, GRAB_IN },
434 {
"right Alt+right Shift host switch",
435 { RIGHT_ALT_DOWN, RIGHT_SHIFT_DOWN, GRAB_OUT, RIGHT_SHIFT_UP, RIGHT_ALT_UP, GRAB_IN },
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 },
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 },
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 },
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 },
466 {
"local shortcut sees pending physical Alt",
467 { CTRL_DOWN, ALT_DOWN, M_DOWN, M_UP, ALT_UP, CTRL_UP },
473static BOOL dispatch(xfContext* xfc, Event event)
475 if (event > END && event < ARRAYSIZE(keyEvents))
477 key(xfc, keyEvents[event].sym, keyEvents[event].down);
483 return focus(xfc, FocusOut, NotifyGrab);
485 return focus(xfc, FocusIn, NotifyUngrab);
487 return focus(xfc, FocusOut, NotifyNormal);
489 return focus(xfc, FocusIn, NotifyNormal);
490 case WHILE_GRABBED_OUT:
491 return focus(xfc, FocusOut, NotifyWhileGrabbed);
493 return xf_generic_MotionNotify(xfc, 10, 20, None, FALSE);
496 return xf_generic_ButtonEvent(xfc, 10, 20, 1, None, FALSE, event == BUTTON_DOWN);
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);
503 xfc->deferredAlt.pending = 0;
511static BOOL test_keyboard_mapping_reload(
void)
514 xfContext xfc = WINPR_C_ARRAY_INIT;
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");
532 (void)fprintf(stderr,
"%s: 1: KeyboardLayout could not be set\n", __func__);
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))
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);
544#if defined(__APPLE__)
545 const DWORD expect = RDP_SCANCODE_OEM_6;
547 const DWORD expect = RDP_SCANCODE_KEY_A;
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))
555 (void)fprintf(stderr,
"%s: 3: xf_keyboard_init failed or %u != %u\n", __func__,
556 xfc.X11_KEYCODE_TO_VIRTUAL_SCANCODE[38], expect);
561 keyboard_keysym = NoSymbol;
562 if (!xf_keyboard_init(&xfc) || (xfc.X11_KEYCODE_TO_VIRTUAL_SCANCODE[38] != expect))
564 (void)fprintf(stderr,
"%s: 4: xf_keyboard_init failed or %u != %u\n", __func__,
565 xfc.X11_KEYCODE_TO_VIRTUAL_SCANCODE[38], expect);
571 xf_keyboard_free(&xfc);
576int main(
int argc,
char* argv[])
580 if (!test_keyboard_mapping_reload())
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;
591 wPubSub* pubSub = PubSub_New(TRUE);
592 if (!settings || !pubSub)
594 (void)fprintf(stderr,
"settings=%p, pubSub=%p\n", (
const void*)settings,
595 (
const void*)pubSub);
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;
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++)
617 memset(xfc.KeyboardState, 0,
sizeof(xfc.KeyboardState));
618 memset(&xfc.deferredAlt, 0,
sizeof(xfc.deferredAlt));
619 memset(physical, 0,
sizeof(physical));
622 failAlt = (cases[i].flags & FAIL_ALT) != 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);
632 (cases[i].flags & UNICODE_MODE) != 0))
634 (void)fprintf(stderr,
"%s: GrabKeyboard or UnicodeInput or %" PRIu32
" != 0\n",
635 cases[i].name, cases[i].flags & UNICODE_MODE);
638 for (
size_t j = 0; j < ARRAYSIZE(cases[i].events) && cases[i].events[j] != END; j++)
640 if (!dispatch(&xfc, cases[i].events[j]))
642 (void)fprintf(stderr,
"%s: event %zu failed\n", cases[i].name, j);
646 if ((cases[i].flags & EXPECT_UNGRAB) && (ungrabs != 1))
648 (void)fprintf(stderr,
"%s: expected one keyboard ungrab, got %u\n", cases[i].name,
652 if ((cases[i].flags & EXPECT_MINIMIZED) && !minimized)
654 (void)fprintf(stderr,
"%s: local shortcut did not minimize\n", cases[i].name);
657 if (strcmp(trace, cases[i].expected) != 0)
659 (void)fprintf(stderr,
"%s: expected '%s', got '%s'\n", cases[i].name, cases[i].expected,
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.