[client] input: gate motion on active capture

This commit is contained in:
Geoffrey McRae
2026-08-06 14:12:43 +10:00
parent a2a185a95b
commit 1baa5a3710
9 changed files with 131 additions and 26 deletions

View File

@@ -189,13 +189,25 @@ static const struct wl_pointer_listener pointerListener = {
static void confinedHandler(void * data, static void confinedHandler(void * data,
struct zwp_confined_pointer_v1 * pointer) struct zwp_confined_pointer_v1 * pointer)
{ {
MTRACE("conf active=1 id=%u", proxyId(pointer)); bool valid;
LG_LOCK(wlWm.surfaceLock);
valid = pointer == wlWm.confinedPointer;
if (valid)
atomic_store_explicit(&wlWm.confActive, true, memory_order_release);
LG_UNLOCK(wlWm.surfaceLock);
MTRACE("conf active=1 id=%u valid=%d", proxyId(pointer), valid);
} }
static void unconfinedHandler(void * data, static void unconfinedHandler(void * data,
struct zwp_confined_pointer_v1 * pointer) struct zwp_confined_pointer_v1 * pointer)
{ {
MTRACE("conf active=0 id=%u", proxyId(pointer)); bool valid;
LG_LOCK(wlWm.surfaceLock);
valid = pointer == wlWm.confinedPointer;
if (valid)
atomic_store_explicit(&wlWm.confActive, false, memory_order_release);
LG_UNLOCK(wlWm.surfaceLock);
MTRACE("conf active=0 id=%u valid=%d", proxyId(pointer), valid);
} }
static const struct zwp_confined_pointer_v1_listener confinedListener = { static const struct zwp_confined_pointer_v1_listener confinedListener = {
@@ -206,13 +218,25 @@ static const struct zwp_confined_pointer_v1_listener confinedListener = {
static void lockedHandler(void * data, static void lockedHandler(void * data,
struct zwp_locked_pointer_v1 * pointer) struct zwp_locked_pointer_v1 * pointer)
{ {
MTRACE("lock active=1 id=%u", proxyId(pointer)); bool valid;
LG_LOCK(wlWm.surfaceLock);
valid = pointer == wlWm.lockedPointer;
if (valid)
atomic_store_explicit(&wlWm.lockActive, true, memory_order_release);
LG_UNLOCK(wlWm.surfaceLock);
MTRACE("lock active=1 id=%u valid=%d", proxyId(pointer), valid);
} }
static void unlockedHandler(void * data, static void unlockedHandler(void * data,
struct zwp_locked_pointer_v1 * pointer) struct zwp_locked_pointer_v1 * pointer)
{ {
MTRACE("lock active=0 id=%u", proxyId(pointer)); bool valid;
LG_LOCK(wlWm.surfaceLock);
valid = pointer == wlWm.lockedPointer;
if (valid)
atomic_store_explicit(&wlWm.lockActive, false, memory_order_release);
LG_UNLOCK(wlWm.surfaceLock);
MTRACE("lock active=0 id=%u valid=%d", proxyId(pointer), valid);
} }
static const struct zwp_locked_pointer_v1_listener lockedListener = { static const struct zwp_locked_pointer_v1_listener lockedListener = {
@@ -421,6 +445,9 @@ static void waylandCleanUpPointer(void)
uint64_t confSeq = 0; uint64_t confSeq = 0;
INTERLOCKED_SECTION(wlWm.surfaceLock, INTERLOCKED_SECTION(wlWm.surfaceLock,
{ {
atomic_store_explicit(&wlWm.lockActive, false, memory_order_release);
atomic_store_explicit(&wlWm.confActive, false, memory_order_release);
if (wlWm.lockedPointer) if (wlWm.lockedPointer)
{ {
lockId = proxyId(wlWm.lockedPointer); lockId = proxyId(wlWm.lockedPointer);
@@ -472,6 +499,9 @@ static void handlePointerCapability(uint32_t capabilities)
zwp_relative_pointer_v1_add_listener(wlWm.relativePointer, zwp_relative_pointer_v1_add_listener(wlWm.relativePointer,
&relativePointerListener, NULL); &relativePointerListener, NULL);
} }
if (app_isCaptureMode())
waylandCapturePointer();
} }
} }
@@ -616,6 +646,8 @@ void waylandGrabPointer(void)
{ {
if (!wlWm.confinedPointer && !wlWm.lockedPointer) if (!wlWm.confinedPointer && !wlWm.lockedPointer)
{ {
atomic_store_explicit(&wlWm.confActive, false,
memory_order_release);
wlWm.confinedPointer = createConfine(NULL); wlWm.confinedPointer = createConfine(NULL);
confId = proxyId(wlWm.confinedPointer); confId = proxyId(wlWm.confinedPointer);
confSeq = app_mouseSeq(); confSeq = app_mouseSeq();
@@ -645,6 +677,7 @@ inline static uint32_t internalUngrabPointer(bool lock, uint64_t * traceSeq)
confId = proxyId(wlWm.confinedPointer); confId = proxyId(wlWm.confinedPointer);
zwp_confined_pointer_v1_destroy(wlWm.confinedPointer); zwp_confined_pointer_v1_destroy(wlWm.confinedPointer);
wlWm.confinedPointer = NULL; wlWm.confinedPointer = NULL;
atomic_store_explicit(&wlWm.confActive, false, memory_order_release);
*traceSeq = app_mouseSeq(); *traceSeq = app_mouseSeq();
} }
@@ -696,9 +729,12 @@ void waylandCapturePointer(void)
confId = proxyId(wlWm.confinedPointer); confId = proxyId(wlWm.confinedPointer);
zwp_confined_pointer_v1_destroy(wlWm.confinedPointer); zwp_confined_pointer_v1_destroy(wlWm.confinedPointer);
wlWm.confinedPointer = NULL; wlWm.confinedPointer = NULL;
atomic_store_explicit(&wlWm.confActive, false,
memory_order_release);
confSeq = app_mouseSeq(); confSeq = app_mouseSeq();
} }
atomic_store_explicit(&wlWm.lockActive, false, memory_order_release);
wlWm.lockedPointer = createLock(); wlWm.lockedPointer = createLock();
lockId = proxyId(wlWm.lockedPointer); lockId = proxyId(wlWm.lockedPointer);
lockSeq = app_mouseSeq(); lockSeq = app_mouseSeq();
@@ -723,6 +759,8 @@ void waylandUncapturePointer(void)
lockId = proxyId(wlWm.lockedPointer); lockId = proxyId(wlWm.lockedPointer);
zwp_locked_pointer_v1_destroy(wlWm.lockedPointer); zwp_locked_pointer_v1_destroy(wlWm.lockedPointer);
wlWm.lockedPointer = NULL; wlWm.lockedPointer = NULL;
atomic_store_explicit(&wlWm.lockActive, false,
memory_order_release);
lockSeq = app_mouseSeq(); lockSeq = app_mouseSeq();
} }
@@ -737,6 +775,8 @@ void waylandUncapturePointer(void)
confDropId = internalUngrabPointer(false, &confDropSeq); confDropId = internalUngrabPointer(false, &confDropSeq);
else if (wlWm.pointer) else if (wlWm.pointer)
{ {
atomic_store_explicit(&wlWm.confActive, false,
memory_order_release);
wlWm.confinedPointer = createConfine(NULL); wlWm.confinedPointer = createConfine(NULL);
confReqId = proxyId(wlWm.confinedPointer); confReqId = proxyId(wlWm.confinedPointer);
confReqSeq = app_mouseSeq(); confReqSeq = app_mouseSeq();
@@ -748,6 +788,13 @@ void waylandUncapturePointer(void)
MLOG(confReqSeq, "conf req id=%u why=uncapture", confReqId); MLOG(confReqSeq, "conf req id=%u why=uncapture", confReqId);
} }
bool waylandIsPointerCaptured(void)
{
const atomic_bool * active = wlWm.warpSupport ?
&wlWm.lockActive : &wlWm.confActive;
return atomic_load_explicit(active, memory_order_acquire);
}
void waylandGrabKeyboard(void) void waylandGrabKeyboard(void)
{ {
if (wlWm.seat && if (wlWm.seat &&

View File

@@ -112,6 +112,8 @@ static bool waylandInit(const LG_DSInitParams params)
LG_LOCK_INIT(wlWm.pendingHDRLock); LG_LOCK_INIT(wlWm.pendingHDRLock);
LG_LOCK_INIT(wlWm.hdrLock); LG_LOCK_INIT(wlWm.hdrLock);
wlWm.desktop = WL_Desktops[0]; wlWm.desktop = WL_Desktops[0];
atomic_init(&wlWm.confActive, false);
atomic_init(&wlWm.lockActive, false);
atomic_init(&wlWm.cmFeaturesDone, false); atomic_init(&wlWm.cmFeaturesDone, false);
atomic_init(&wlWm.cmCanDoHDR, false); atomic_init(&wlWm.cmCanDoHDR, false);
atomic_init(&wlWm.hdrPQWhiteLevel, 203); atomic_init(&wlWm.hdrPQWhiteLevel, 203);
@@ -321,6 +323,7 @@ struct LG_DisplayServerOps LGDS_Wayland =
.ungrabPointer = waylandUngrabPointer, .ungrabPointer = waylandUngrabPointer,
.capturePointer = waylandCapturePointer, .capturePointer = waylandCapturePointer,
.uncapturePointer = waylandUncapturePointer, .uncapturePointer = waylandUncapturePointer,
.isPointerCaptured = waylandIsPointerCaptured,
.grabKeyboard = waylandGrabKeyboard, .grabKeyboard = waylandGrabKeyboard,
.ungrabKeyboard = waylandUngrabKeyboard, .ungrabKeyboard = waylandUngrabKeyboard,
.getKeyLabel = waylandGetKeyLabel, .getKeyLabel = waylandGetKeyLabel,

View File

@@ -123,10 +123,11 @@ struct WaylandHDRParameters
struct WaylandDSState struct WaylandDSState
{ {
bool pointerGrabbed; _Atomic(bool) confActive;
bool keyboardGrabbed; _Atomic(bool) lockActive;
bool pointerInSurface; bool keyboardGrabbed;
bool focusedOnSurface; bool pointerInSurface;
bool focusedOnSurface;
WL_DesktopOps * desktop; WL_DesktopOps * desktop;
@@ -378,6 +379,7 @@ void waylandUngrabKeyboard(void);
void waylandUngrabPointer(void); void waylandUngrabPointer(void);
void waylandCapturePointer(void); void waylandCapturePointer(void);
void waylandUncapturePointer(void); void waylandUncapturePointer(void);
bool waylandIsPointerCaptured(void);
void waylandRealignPointer(void); void waylandRealignPointer(void);
void waylandWarpPointer(int x, int y, bool exiting); void waylandWarpPointer(int x, int y, bool exiting);
void waylandGuestPointerUpdated(double x, double y, double localX, double localY); void waylandGuestPointerUpdated(double x, double y, double localX, double localY);

View File

@@ -1940,6 +1940,8 @@ static void x11GrabPointer(void)
static void x11UngrabPointer(void) static void x11UngrabPointer(void)
{ {
atomic_store_explicit(&x11.captureActive, false, memory_order_release);
if (!x11.pointerGrabbed) if (!x11.pointerGrabbed)
return; return;
@@ -1952,10 +1954,14 @@ static void x11UngrabPointer(void)
static void x11CapturePointer(void) static void x11CapturePointer(void)
{ {
x11GrabPointer(); x11GrabPointer();
atomic_store_explicit(&x11.captureActive, x11.pointerGrabbed,
memory_order_release);
} }
static void x11UncapturePointer(void) static void x11UncapturePointer(void)
{ {
atomic_store_explicit(&x11.captureActive, false, memory_order_release);
/* we need to ungrab the pointer on the following conditions when exiting capture mode: /* we need to ungrab the pointer on the following conditions when exiting capture mode:
* - if the format is invalid as we do not know where the guest cursor is, * - if the format is invalid as we do not know where the guest cursor is,
* which breaks edge detection as the cursor can not be warped out of the * which breaks edge detection as the cursor can not be warped out of the
@@ -1966,6 +1972,11 @@ static void x11UncapturePointer(void)
x11UngrabPointer(); x11UngrabPointer();
} }
static bool x11IsPointerCaptured(void)
{
return atomic_load_explicit(&x11.captureActive, memory_order_acquire);
}
static void x11GrabKeyboard(void) static void x11GrabKeyboard(void)
{ {
if (x11.keyboardGrabbed) if (x11.keyboardGrabbed)
@@ -2143,6 +2154,7 @@ struct LG_DisplayServerOps LGDS_X11 =
.ungrabPointer = x11UngrabPointer, .ungrabPointer = x11UngrabPointer,
.capturePointer = x11CapturePointer, .capturePointer = x11CapturePointer,
.uncapturePointer = x11UncapturePointer, .uncapturePointer = x11UncapturePointer,
.isPointerCaptured = x11IsPointerCaptured,
.getKeyLabel = x11GetKeyLabel, .getKeyLabel = x11GetKeyLabel,
.grabKeyboard = x11GrabKeyboard, .grabKeyboard = x11GrabKeyboard,
.ungrabKeyboard = x11UngrabKeyboard, .ungrabKeyboard = x11UngrabKeyboard,

View File

@@ -93,8 +93,9 @@ struct X11DSState
int xValuator; int xValuator;
int yValuator; int yValuator;
bool pointerGrabbed; _Atomic(bool) captureActive;
bool keyboardGrabbed; bool pointerGrabbed;
bool keyboardGrabbed;
bool entered; bool entered;
bool focused; bool focused;
bool fullscreen; bool fullscreen;

View File

@@ -210,6 +210,7 @@ struct LG_DisplayServerOps
/* (un)capturePointer is used do toggle special cursor tracking in capture mode */ /* (un)capturePointer is used do toggle special cursor tracking in capture mode */
void (*capturePointer)(void); void (*capturePointer)(void);
void (*uncapturePointer)(void); void (*uncapturePointer)(void);
bool (*isPointerCaptured)(void);
/* get the active keymap's display label for the provided Linux keycode */ /* get the active keymap's display label for the provided Linux keycode */
bool (*getKeyLabel)(int sc, char * label, size_t size); bool (*getKeyLabel)(int sc, char * label, size_t size);
@@ -291,6 +292,7 @@ struct LG_DisplayServerOps
DEBUG_ASSERT((x)->ungrabPointer ); \ DEBUG_ASSERT((x)->ungrabPointer ); \
DEBUG_ASSERT((x)->capturePointer ); \ DEBUG_ASSERT((x)->capturePointer ); \
DEBUG_ASSERT((x)->uncapturePointer ); \ DEBUG_ASSERT((x)->uncapturePointer ); \
DEBUG_ASSERT((x)->isPointerCaptured ); \
DEBUG_ASSERT((x)->getKeyLabel ); \ DEBUG_ASSERT((x)->getKeyLabel ); \
DEBUG_ASSERT((x)->warpPointer ); \ DEBUG_ASSERT((x)->warpPointer ); \
DEBUG_ASSERT((x)->realignPointer ); \ DEBUG_ASSERT((x)->realignPointer ); \

View File

@@ -530,9 +530,10 @@ void core_handleGuestMouseUpdate(void)
void core_handleMouseGrabbed(double ex, double ey) void core_handleMouseGrabbed(double ex, double ey)
{ {
const bool enabled = core_inputEnabled(); const bool active = g_cursor.grab && g_state.ds->isPointerCaptured();
MTRACE("captured delta=%.3f,%.3f enabled=%d", ex, ey, const bool enabled = active && core_inputEnabled();
enabled); MTRACE("captured delta=%.3f,%.3f active=%d enabled=%d", ex, ey,
active, enabled);
if (!enabled) if (!enabled)
return; return;

View File

@@ -73,6 +73,7 @@ set(MOUSE_CASES
capture-pending capture-pending
capture-revoked capture-revoked
capture-release capture-release
capture-fallback
rotate-scale rotate-scale
geometry geometry
edges edges

View File

@@ -135,6 +135,12 @@ static void ungrab(void)
static void capture(void) static void capture(void)
{ {
if (m.support == LG_DS_WARP_NONE)
{
grab();
return;
}
m.conf.req = false; m.conf.req = false;
if (!m.conf.hold) if (!m.conf.hold)
m.conf.on = false; m.conf.on = false;
@@ -144,12 +150,23 @@ static void capture(void)
static void uncapture(void) static void uncapture(void)
{ {
if (m.support == LG_DS_WARP_NONE)
{
ungrab();
return;
}
push(EV_UNCAPTURE, 0, 0, false); push(EV_UNCAPTURE, 0, 0, false);
m.lock.req = false; m.lock.req = false;
if (!m.lock.hold) if (!m.lock.hold)
m.lock.on = false; m.lock.on = false;
} }
static bool captured(void)
{
return m.support == LG_DS_WARP_NONE ? m.conf.on : m.lock.on;
}
static void warp(int x, int y, bool exiting) static void warp(int x, int y, bool exiting)
{ {
push(EV_WARP, x, y, exiting); push(EV_WARP, x, y, exiting);
@@ -171,6 +188,7 @@ static struct LG_DisplayServerOps ds = {
.ungrabPointer = ungrab, .ungrabPointer = ungrab,
.capturePointer = capture, .capturePointer = capture,
.uncapturePointer = uncapture, .uncapturePointer = uncapture,
.isPointerCaptured = captured,
.warpPointer = warp, .warpPointer = warp,
.isValidPointerPos = valid, .isValidPointerPos = valid,
}; };
@@ -407,6 +425,23 @@ static void testCapRelease(void)
CHECK(count(EV_MOTION) == 1); CHECK(count(EV_MOTION) == 1);
} }
static void testCapFallback(void)
{
reset();
setLocal(50, 50);
m.support = LG_DS_WARP_NONE;
g_params.captureInputOnly = true;
core_setGrabQuiet(true);
m.conf.on = true;
core_handleMouseGrabbed(1, 0);
CHECK(count(EV_MOTION) == 1);
m.conf.on = false;
core_handleMouseGrabbed(1, 0);
CHECK(count(EV_MOTION) == 1);
}
static void testRotateScale(void) static void testRotateScale(void)
{ {
reset(); reset();
@@ -634,19 +669,20 @@ struct Test
}; };
static const struct Test tests[] = { static const struct Test tests[] = {
{ "inset-exit" , testInsetExit }, { "inset-exit" , testInsetExit },
{ "exit-delay" , testExitWait }, { "exit-delay" , testExitWait },
{ "exit-guest" , testExitGuest }, { "exit-guest" , testExitGuest },
{ "confine-pending", testConfWait }, { "confine-pending" , testConfWait },
{ "confine-guest" , testConfGuest }, { "confine-guest" , testConfGuest },
{ "capture-pending", testCapWait }, { "capture-pending" , testCapWait },
{ "capture-revoked", testCapRevoke }, { "capture-revoked" , testCapRevoke },
{ "capture-release", testCapRelease }, { "capture-release" , testCapRelease },
{ "rotate-scale" , testRotateScale }, { "capture-fallback", testCapFallback },
{ "geometry" , testGeometry }, { "rotate-scale" , testRotateScale },
{ "edges" , testEdges }, { "geometry" , testGeometry },
{ "scales" , testScales }, { "edges" , testEdges },
{ "border-exit" , testBorderExit }, { "scales" , testScales },
{ "border-exit" , testBorderExit },
}; };
int main(int argc, char ** argv) int main(int argc, char ** argv)