[client] X11: test grabbed input routing
Some checks are pending
build / client (Debug, map[cc:clang cxx:clang++], libdecor) (push) Waiting to run
build / client (Debug, map[cc:clang cxx:clang++], xdg-shell) (push) Waiting to run
build / client (Debug, map[cc:gcc cxx:g++], libdecor) (push) Waiting to run
build / client (Debug, map[cc:gcc cxx:g++], xdg-shell) (push) Waiting to run
build / client (Release, map[cc:clang cxx:clang++], libdecor) (push) Waiting to run
build / client (Release, map[cc:clang cxx:clang++], xdg-shell) (push) Waiting to run
build / client (Release, map[cc:gcc cxx:g++], libdecor) (push) Waiting to run
build / client (Release, map[cc:gcc cxx:g++], xdg-shell) (push) Waiting to run
build / module (push) Waiting to run
build / host-linux (push) Waiting to run
build / host-windows-cross (push) Waiting to run
build / host-windows-native (push) Waiting to run
build / idd (push) Waiting to run
build / obs (clang) (push) Waiting to run
build / obs (gcc) (push) Waiting to run
build / docs (push) Waiting to run

Keep raw and cooked event selection inside the tested X11 policy.

Exercise grab transitions and held-key normalization directly.
This commit is contained in:
Geoffrey McRae
2026-08-10 17:56:23 +10:00
parent 15e0cbbcc4
commit 4afb0254d1
6 changed files with 258 additions and 87 deletions

View File

@@ -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;
}

View File

@@ -23,6 +23,7 @@
#include <stdbool.h>
#include <stddef.h>
#include <stdatomic.h>
#include <stdint.h>
#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

View File

@@ -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)

View File

@@ -104,8 +104,6 @@ struct X11DSState
X11Input input;
LG_Lock pointerLock;
_Atomic(bool) captureActive;
_Atomic(bool) pointerGrabbed;
bool keyboardGrabbed;
bool fullscreen;
struct Rect rect;