diff --git a/client/displayservers/X11/input_event.c b/client/displayservers/X11/input_event.c index ce394e08..e31eb11c 100644 --- a/client/displayservers/X11/input_event.c +++ b/client/displayservers/X11/input_event.c @@ -38,10 +38,36 @@ void x11InputInit(X11Input * input, const LG_DSInputSink * sink, void * opaque) { memset(input, 0, sizeof(*input)); + atomic_init(&input->pointerGrabbed, false); + atomic_init(&input->keyboardGrabbed, false); input->sink = sink; input->opaque = opaque; } +void x11InputSetPointerGrabbed(X11Input * input, bool grabbed) +{ + atomic_store_explicit(&input->pointerGrabbed, grabbed, + memory_order_release); +} + +bool x11InputIsPointerGrabbed(const X11Input * input) +{ + return atomic_load_explicit(&input->pointerGrabbed, + memory_order_acquire); +} + +void x11InputSetKeyboardGrabbed(X11Input * input, bool grabbed) +{ + atomic_store_explicit(&input->keyboardGrabbed, grabbed, + memory_order_release); +} + +bool x11InputIsKeyboardGrabbed(const X11Input * input) +{ + return atomic_load_explicit(&input->keyboardGrabbed, + memory_order_acquire); +} + bool x11InputFocus(X11Input * input, bool focused, const uint32_t * keys, size_t count) { @@ -90,7 +116,7 @@ void x11InputPointerMotion(X11Input * input, double x, double y) void x11InputPointerButton(X11Input * input, unsigned int detail, bool pressed, bool raw) { - if (!raw && !input->entered) + if (!input->entered || raw != x11InputIsPointerGrabbed(input)) return; const unsigned int button = mapButton(detail); @@ -123,24 +149,25 @@ void x11InputPointerButton(X11Input * input, unsigned int detail, void x11InputRelativeMotion(X11Input * input, double x, double y, double rawX, double rawY) { + if (!input->entered || !x11InputIsPointerGrabbed(input)) + return; + input->sink->relative(input->opaque, x, y, rawX, rawY); } -void x11InputKeyboardKey(X11Input * input, unsigned int keycode, - int minKeycode, bool pressed, bool raw, bool inputActive, - const char * text) +bool x11InputKeyboardKey(X11Input * input, unsigned int keycode, + int minKeycode, bool pressed, bool raw) { - if (raw) - { - if (!inputActive) - return; - } - else if (!input->focused) - return; + if (!input->focused || raw != x11InputIsKeyboardGrabbed(input)) + return false; input->sink->key(input->opaque, (int)keycode - minKeycode, pressed); + return true; +} - if (!raw && pressed && text) +void x11InputKeyboardText(X11Input * input, const char * text) +{ + if (text && *text) input->sink->text(input->opaque, text); } @@ -151,3 +178,29 @@ void x11InputKeyboardState(X11Input * input, input->sink->modifiers(input->opaque, ctrl, shift, alt, super); input->sink->leds(input->opaque, numLock, capsLock, scrollLock); } + +size_t x11InputHeldKeys( + const char keymap[X11_INPUT_KEYMAP_SIZE], + int minKeycode, int maxKeycode, + uint32_t * keys, size_t capacity) +{ + if (minKeycode < 0 || maxKeycode < minKeycode) + return 0; + + size_t count = 0; + for (int keycode = minKeycode; + keycode <= maxKeycode && keycode < X11_INPUT_KEYMAP_SIZE * 8; + ++keycode) + { + const unsigned char keyByte = (unsigned char)keymap[keycode / 8]; + if (!(keyByte & (1U << (keycode % 8)))) + continue; + + if (count == capacity) + break; + + keys[count++] = keycode - minKeycode; + } + + return count; +} diff --git a/client/displayservers/X11/input_event.h b/client/displayservers/X11/input_event.h index d1f0fed0..71df7425 100644 --- a/client/displayservers/X11/input_event.h +++ b/client/displayservers/X11/input_event.h @@ -23,6 +23,7 @@ #include #include +#include #include #include "../input.h" @@ -32,14 +33,23 @@ typedef struct X11Input const LG_DSInputSink * sink; void * opaque; uint32_t buttons; + _Atomic(bool) pointerGrabbed; + _Atomic(bool) keyboardGrabbed; bool entered; bool focused; } X11Input; +#define X11_INPUT_KEYMAP_SIZE 32 + void x11InputInit(X11Input * input, const LG_DSInputSink * sink, void * opaque); +void x11InputSetPointerGrabbed(X11Input * input, bool grabbed); +bool x11InputIsPointerGrabbed(const X11Input * input); +void x11InputSetKeyboardGrabbed(X11Input * input, bool grabbed); +bool x11InputIsKeyboardGrabbed(const X11Input * input); + bool x11InputFocus(X11Input * input, bool focused, const uint32_t * keys, size_t count); bool x11InputPointerEnter(X11Input * input, bool mainWindow, @@ -51,11 +61,15 @@ void x11InputPointerButton(X11Input * input, unsigned int button, bool pressed, bool raw); void x11InputRelativeMotion(X11Input * input, double x, double y, double rawX, double rawY); -void x11InputKeyboardKey(X11Input * input, unsigned int keycode, - int minKeycode, bool pressed, bool raw, bool inputActive, - const char * text); +bool x11InputKeyboardKey(X11Input * input, unsigned int keycode, + int minKeycode, bool pressed, bool raw); +void x11InputKeyboardText(X11Input * input, const char * text); void x11InputKeyboardState(X11Input * input, bool ctrl, bool shift, bool alt, bool super, bool numLock, bool capsLock, bool scrollLock); +size_t x11InputHeldKeys( + const char keymap[X11_INPUT_KEYMAP_SIZE], + int minKeycode, int maxKeycode, + uint32_t * keys, size_t capacity); #endif diff --git a/client/displayservers/X11/x11.c b/client/displayservers/X11/x11.c index 5d37731f..fc0c6ec8 100644 --- a/client/displayservers/X11/x11.c +++ b/client/displayservers/X11/x11.c @@ -443,7 +443,6 @@ static bool x11Init(const LG_DSInitParams params) x11InputInit(&x11.input, &inputSink, NULL); LG_LOCK_INIT(x11.pointerLock); atomic_init(&x11.captureActive, false); - atomic_init(&x11.pointerGrabbed, false); x11.xValuator = -1; x11.yValuator = -1; x11.numLockIndicator = -1; @@ -1363,19 +1362,15 @@ static void setFocus(bool focused) { x11UpdateKeyboardGroup(); - char keymap[32] = { 0 }; + char keymap[X11_INPUT_KEYMAP_SIZE] = { 0 }; XQueryKeymap(x11.display, keymap); memset(x11.modifiers, 0, sizeof(x11.modifiers)); - for (int keycode = x11.minKeycode; - keycode <= x11.maxKeycode && keycode < 256; ++keycode) + count = x11InputHeldKeys(keymap, x11.minKeycode, x11.maxKeycode, + keys, ARRAY_LENGTH(keys)); + for (size_t i = 0; i < count; ++i) { - const unsigned char keyByte = keymap[keycode / 8]; - if (!(keyByte & (1U << (keycode % 8)))) - continue; - - keys[count++] = keycode - x11.minKeycode; - + const unsigned int keycode = keys[i] + x11.minKeycode; const KeySym sym = XkbKeycodeToKeysym(x11.display, keycode, atomic_load(&x11.keyboardGroup), 0); const int modifier = keySymToModifier(sym); @@ -1384,7 +1379,7 @@ static void setFocus(bool focused) } } - x11InputFocus(&x11.input, focused, keys, count); + x11InputFocus(&x11.input, focused, focused ? keys : NULL, count); if (focused) updateKeyboardState(); } @@ -1519,13 +1514,11 @@ static void x11XInputEvent(XGenericEventCookie *cookie) case XI_KeyPress: { - if (!x11.input.focused || x11.keyboardGrabbed) - return; - XIDeviceEvent *device = cookie->data; atomic_store(&x11.keyboardGroup, device->group.effective); - x11InputKeyboardKey(&x11.input, device->detail, x11.minKeycode, - true, false, true, NULL); + if (!x11InputKeyboardKey(&x11.input, device->detail, + x11.minKeycode, true, false)) + return; if (x11.xic && app_isOverlayMode()) { @@ -1548,7 +1541,7 @@ static void x11XInputEvent(XGenericEventCookie *cookie) else if (status == XLookupChars || status == XLookupBoth) { buffer[count] = '\0'; - inputText(x11.input.opaque, buffer); + x11InputKeyboardText(&x11.input, buffer); } } @@ -1558,36 +1551,33 @@ static void x11XInputEvent(XGenericEventCookie *cookie) case XI_KeyRelease: { - if (!x11.input.focused || x11.keyboardGrabbed) + XIDeviceEvent *device = cookie->data; + if (!x11InputKeyboardKey(&x11.input, device->detail, + x11.minKeycode, false, false)) return; - XIDeviceEvent *device = cookie->data; - x11InputKeyboardKey(&x11.input, device->detail, x11.minKeycode, - false, false, true, NULL); updateKeyState(device->detail, false); return; } case XI_RawKeyPress: { - if (!x11.input.focused || !x11.keyboardGrabbed) + XIRawEvent *raw = cookie->data; + if (!x11InputKeyboardKey(&x11.input, raw->detail, + x11.minKeycode, true, true)) return; - XIRawEvent *raw = cookie->data; - x11InputKeyboardKey(&x11.input, raw->detail, x11.minKeycode, - true, true, true, NULL); updateKeyState(raw->detail, true); return; } case XI_RawKeyRelease: { - if (!x11.input.focused || !x11.keyboardGrabbed) + XIRawEvent *raw = cookie->data; + if (!x11InputKeyboardKey(&x11.input, raw->detail, + x11.minKeycode, false, true)) return; - XIRawEvent *raw = cookie->data; - x11InputKeyboardKey(&x11.input, raw->detail, x11.minKeycode, - false, true, true, NULL); updateKeyState(raw->detail, false); return; } @@ -1608,10 +1598,6 @@ static void x11XInputEvent(XGenericEventCookie *cookie) case XI_RawButtonPress: { - if (!x11.input.entered || - !atomic_load_explicit(&x11.pointerGrabbed, memory_order_acquire)) - return; - XIRawEvent *raw = cookie->data; x11InputPointerButton(&x11.input, raw->detail, true, true); return; @@ -1619,10 +1605,6 @@ static void x11XInputEvent(XGenericEventCookie *cookie) case XI_RawButtonRelease: { - if (!x11.input.entered || - !atomic_load_explicit(&x11.pointerGrabbed, memory_order_acquire)) - return; - XIRawEvent *raw = cookie->data; x11InputPointerButton(&x11.input, raw->detail, false, true); return; @@ -1637,10 +1619,6 @@ static void x11XInputEvent(XGenericEventCookie *cookie) case XI_RawMotion: { - if (!x11.input.entered || - !atomic_load_explicit(&x11.pointerGrabbed, memory_order_acquire)) - return; - XIRawEvent *raw = cookie->data; double raw_axis[2] = { 0 }; double axis[2] = { 0 }; @@ -2027,7 +2005,7 @@ static void x11PrintGrabError(const char * type, int dev, Status ret) static bool x11GrabPointerLocked(void) { - if (atomic_load_explicit(&x11.pointerGrabbed, memory_order_acquire)) + if (x11InputIsPointerGrabbed(&x11.input)) return true; unsigned char mask_bits[XIMaskLen(XI_LASTEVENT)] = { 0 }; @@ -2076,7 +2054,7 @@ static bool x11GrabPointerLocked(void) return false; } - atomic_store_explicit(&x11.pointerGrabbed, true, memory_order_release); + x11InputSetPointerGrabbed(&x11.input, true); return true; } @@ -2089,7 +2067,7 @@ static void x11GrabPointer(void) static void x11UngrabPointerLocked(void) { - if (!atomic_load_explicit(&x11.pointerGrabbed, memory_order_acquire)) + if (!x11InputIsPointerGrabbed(&x11.input)) { app_handleGrabEvent(false); return; @@ -2098,7 +2076,7 @@ static void x11UngrabPointerLocked(void) XIUngrabDevice(x11.display, x11.pointerDev, CurrentTime); XSync(x11.display, False); - atomic_store_explicit(&x11.pointerGrabbed, false, memory_order_release); + x11InputSetPointerGrabbed(&x11.input, false); app_handleGrabEvent(false); } @@ -2112,7 +2090,7 @@ static void x11UngrabPointer(void) static bool x11IsPointerGrabbed(void) { - return atomic_load_explicit(&x11.pointerGrabbed, memory_order_acquire); + return x11InputIsPointerGrabbed(&x11.input); } static void x11CapturePointer(void) @@ -2148,7 +2126,7 @@ static bool x11IsPointerCaptured(void) static void x11GrabKeyboard(void) { - if (x11.keyboardGrabbed) + if (x11InputIsKeyboardGrabbed(&x11.input)) return; unsigned char mask_bits[XIMaskLen (XI_LASTEVENT)] = { 0 }; @@ -2178,18 +2156,18 @@ static void x11GrabKeyboard(void) return; } - x11.keyboardGrabbed = true; + x11InputSetKeyboardGrabbed(&x11.input, true); } static void x11UngrabKeyboard(void) { - if (!x11.keyboardGrabbed) + if (!x11InputIsKeyboardGrabbed(&x11.input)) return; XIUngrabDevice(x11.display, x11.keyboardDev, CurrentTime); XSync(x11.display, False); - x11.keyboardGrabbed = false; + x11InputSetKeyboardGrabbed(&x11.input, false); } static void x11WarpPointer(int x, int y, bool exiting) diff --git a/client/displayservers/X11/x11.h b/client/displayservers/X11/x11.h index 783b6a8e..269e450e 100644 --- a/client/displayservers/X11/x11.h +++ b/client/displayservers/X11/x11.h @@ -104,8 +104,6 @@ struct X11DSState X11Input input; LG_Lock pointerLock; _Atomic(bool) captureActive; - _Atomic(bool) pointerGrabbed; - bool keyboardGrabbed; bool fullscreen; struct Rect rect; diff --git a/client/tests/CMakeLists.txt b/client/tests/CMakeLists.txt index f5bf1283..dd163587 100644 --- a/client/tests/CMakeLists.txt +++ b/client/tests/CMakeLists.txt @@ -87,6 +87,15 @@ foreach(name IN ITEMS wheel-residual wheel-multiple) ) endforeach() +foreach(name IN ITEMS pointer-routing keyboard-routing held-keys) + add_test(NAME display-input-x11-${name} + COMMAND displayserver-input-tests x11 ${name} + ) + set_tests_properties(display-input-x11-${name} PROPERTIES + TIMEOUT 10 + ) +endforeach() + add_executable(mouse-tests mouse_test.c ../src/core.c diff --git a/client/tests/displayserver_input_test.c b/client/tests/displayserver_input_test.c index c124217e..ead75afb 100644 --- a/client/tests/displayserver_input_test.c +++ b/client/tests/displayserver_input_test.c @@ -523,8 +523,11 @@ static void relativeMotion(struct Fixture * fixture, bool active, if (fixture->backend == BACKEND_WAYLAND) wlInputRelativeMotion(&fixture->input.wayland, active, x, y, rawX, rawY); - else if (active) + else + { + x11InputSetPointerGrabbed(&fixture->input.x11, active); x11InputRelativeMotion(&fixture->input.x11, x, y, rawX, rawY); + } } static void keyboardEnter(struct Fixture * fixture, bool mainSurface, @@ -545,13 +548,17 @@ static void keyboardLeave(struct Fixture * fixture, bool mainSurface) } static void keyboardKey(struct Fixture * fixture, unsigned int key, - bool pressed, const char * text, bool inputActive) + bool pressed, const char * text) { if (fixture->backend == BACKEND_WAYLAND) wlInputKeyboardKey(&fixture->input.wayland, key, pressed, text); else - x11InputKeyboardKey(&fixture->input.x11, key + 8, 8, pressed, - false, inputActive, text); + { + const bool accepted = x11InputKeyboardKey( + &fixture->input.x11, key + 8, 8, pressed, false); + if (accepted && pressed) + x11InputKeyboardText(&fixture->input.x11, text); + } } static void keyboardState(struct Fixture * fixture, @@ -759,31 +766,140 @@ static void testKeyboardKeys(enum Backend backend) struct Fixture fixture; initFixture(&fixture, backend); - keyboardKey(&fixture, 30, true, "ignored", true); - keyboardKey(&fixture, 30, false, "ignored", true); + keyboardKey(&fixture, 30, true, "ignored"); + keyboardKey(&fixture, 30, false, "ignored"); CHECK(fixture.trace.count == 0); keyboardEnter(&fixture, true, NULL, 0); clearTrace(&fixture); - keyboardKey(&fixture, 30, true, "a", true); - keyboardKey(&fixture, 30, false, "ignored", true); + keyboardKey(&fixture, 30, true, "a"); + keyboardKey(&fixture, 30, false, "ignored"); + keyboardKey(&fixture, 31, true, ""); + keyboardKey(&fixture, 31, false, NULL); static const struct Trace expected[] = { KEY(30, true), TEXT("a"), KEY(30, false), + KEY(31, true), + KEY(31, false), }; expectTrace(&fixture, expected, ARRAY_LENGTH(expected)); keyboardLeave(&fixture, true); clearTrace(&fixture); - keyboardKey(&fixture, 31, true, "s", true); - keyboardKey(&fixture, 31, false, NULL, true); + keyboardKey(&fixture, 31, true, "s"); + keyboardKey(&fixture, 31, false, NULL); CHECK(fixture.trace.count == 0); } +static void testX11PointerRouting(enum Backend backend) +{ + CHECK(backend == BACKEND_X11); + + struct Fixture fixture; + initFixture(&fixture, backend); + pointerEnter(&fixture, true, 40.0, 30.0); + clearTrace(&fixture); + + x11InputPointerButton(&fixture.input.x11, 1, true, true); + x11InputRelativeMotion(&fixture.input.x11, 1.0, 2.0, 3.0, 4.0); + + x11InputSetPointerGrabbed(&fixture.input.x11, true); + x11InputPointerButton(&fixture.input.x11, 1, true, false); + x11InputPointerButton(&fixture.input.x11, 1, true, true); + x11InputRelativeMotion(&fixture.input.x11, 1.0, 2.0, 3.0, 4.0); + + x11InputSetPointerGrabbed(&fixture.input.x11, false); + x11InputPointerButton(&fixture.input.x11, 1, false, true); + x11InputRelativeMotion(&fixture.input.x11, 1.0, 2.0, 3.0, 4.0); + x11InputPointerButton(&fixture.input.x11, 1, false, false); + + static const struct Trace expected[] = + { + BUTTON(1, true), + RELATIVE(1.0, 2.0, 3.0, 4.0), + BUTTON(1, false), + }; + expectTrace(&fixture, expected, ARRAY_LENGTH(expected)); + CHECK(fixture.input.x11.buttons == 0); +} + +static void testX11KeyboardRouting(enum Backend backend) +{ + CHECK(backend == BACKEND_X11); + + struct Fixture fixture; + initFixture(&fixture, backend); + keyboardEnter(&fixture, true, NULL, 0); + clearTrace(&fixture); + + CHECK(!x11InputKeyboardKey(&fixture.input.x11, + 30 + 8, 8, true, true)); + CHECK(x11InputKeyboardKey(&fixture.input.x11, + 30 + 8, 8, true, false)); + x11InputKeyboardText(&fixture.input.x11, "a"); + + x11InputSetKeyboardGrabbed(&fixture.input.x11, true); + CHECK(!x11InputKeyboardKey(&fixture.input.x11, + 31 + 8, 8, true, false)); + CHECK(x11InputKeyboardKey(&fixture.input.x11, + 30 + 8, 8, false, true)); + CHECK(x11InputKeyboardKey(&fixture.input.x11, + 31 + 8, 8, true, true)); + + x11InputSetKeyboardGrabbed(&fixture.input.x11, false); + CHECK(!x11InputKeyboardKey(&fixture.input.x11, + 31 + 8, 8, false, true)); + CHECK(x11InputKeyboardKey(&fixture.input.x11, + 31 + 8, 8, false, false)); + + static const struct Trace expected[] = + { + KEY(30, true), + TEXT("a"), + KEY(30, false), + KEY(31, true), + KEY(31, false), + }; + expectTrace(&fixture, expected, ARRAY_LENGTH(expected)); +} + +static void setKeymapKey(char keymap[X11_INPUT_KEYMAP_SIZE], + unsigned int keycode) +{ + CHECK(keycode < X11_INPUT_KEYMAP_SIZE * 8); + keymap[keycode / 8] = (char)( + (unsigned char)keymap[keycode / 8] | (1U << (keycode % 8))); +} + +static void testX11HeldKeys(enum Backend backend) +{ + CHECK(backend == BACKEND_X11); + + char keymap[X11_INPUT_KEYMAP_SIZE] = { 0 }; + setKeymapKey(keymap, 7); + setKeymapKey(keymap, 8); + setKeymapKey(keymap, 30); + setKeymapKey(keymap, 42); + setKeymapKey(keymap, 43); + + uint32_t keys[4]; + size_t count = x11InputHeldKeys(keymap, 8, 42, + keys, ARRAY_LENGTH(keys)); + CHECK(count == 3); + CHECK(keys[0] == 0); + CHECK(keys[1] == 22); + CHECK(keys[2] == 34); + + count = x11InputHeldKeys(keymap, 8, 42, keys, 2); + CHECK(count == 2); + CHECK(keys[0] == 0); + CHECK(keys[1] == 22); +} + static void testKeyboardState(enum Backend backend) { struct Fixture fixture; @@ -814,16 +930,19 @@ struct Test static const struct Test tests[] = { - { "pointer-events" , testPointerEvents }, - { "pointer-buttons", testPointerButtons }, - { "pointer-release", testPointerRelease }, - { "wheel-steps" , testWheelSteps }, - { "wheel-residual" , testWheelResidual }, - { "wheel-multiple" , testWheelMultiple }, - { "relative-motion", testRelativeMotion }, - { "keyboard-focus" , testKeyboardFocus }, - { "keyboard-keys" , testKeyboardKeys }, - { "keyboard-state" , testKeyboardState }, + { "pointer-events" , testPointerEvents }, + { "pointer-buttons" , testPointerButtons }, + { "pointer-release" , testPointerRelease }, + { "wheel-steps" , testWheelSteps }, + { "wheel-residual" , testWheelResidual }, + { "wheel-multiple" , testWheelMultiple }, + { "relative-motion" , testRelativeMotion }, + { "keyboard-focus" , testKeyboardFocus }, + { "keyboard-keys" , testKeyboardKeys }, + { "keyboard-state" , testKeyboardState }, + { "pointer-routing" , testX11PointerRouting }, + { "keyboard-routing", testX11KeyboardRouting }, + { "held-keys" , testX11HeldKeys }, }; static bool parseBackend(const char * name, enum Backend * backend)