diff --git a/client/displayservers/Wayland/input.c b/client/displayservers/Wayland/input.c index a8dc91f2..30238149 100644 --- a/client/displayservers/Wayland/input.c +++ b/client/displayservers/Wayland/input.c @@ -189,13 +189,25 @@ static const struct wl_pointer_listener pointerListener = { static void confinedHandler(void * data, 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, 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 = { @@ -206,13 +218,25 @@ static const struct zwp_confined_pointer_v1_listener confinedListener = { static void lockedHandler(void * data, 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, 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 = { @@ -421,6 +445,9 @@ static void waylandCleanUpPointer(void) uint64_t confSeq = 0; 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) { lockId = proxyId(wlWm.lockedPointer); @@ -472,6 +499,9 @@ static void handlePointerCapability(uint32_t capabilities) zwp_relative_pointer_v1_add_listener(wlWm.relativePointer, &relativePointerListener, NULL); } + + if (app_isCaptureMode()) + waylandCapturePointer(); } } @@ -616,6 +646,8 @@ void waylandGrabPointer(void) { if (!wlWm.confinedPointer && !wlWm.lockedPointer) { + atomic_store_explicit(&wlWm.confActive, false, + memory_order_release); wlWm.confinedPointer = createConfine(NULL); confId = proxyId(wlWm.confinedPointer); confSeq = app_mouseSeq(); @@ -645,6 +677,7 @@ inline static uint32_t internalUngrabPointer(bool lock, uint64_t * traceSeq) confId = proxyId(wlWm.confinedPointer); zwp_confined_pointer_v1_destroy(wlWm.confinedPointer); wlWm.confinedPointer = NULL; + atomic_store_explicit(&wlWm.confActive, false, memory_order_release); *traceSeq = app_mouseSeq(); } @@ -696,9 +729,12 @@ void waylandCapturePointer(void) confId = proxyId(wlWm.confinedPointer); zwp_confined_pointer_v1_destroy(wlWm.confinedPointer); wlWm.confinedPointer = NULL; + atomic_store_explicit(&wlWm.confActive, false, + memory_order_release); confSeq = app_mouseSeq(); } + atomic_store_explicit(&wlWm.lockActive, false, memory_order_release); wlWm.lockedPointer = createLock(); lockId = proxyId(wlWm.lockedPointer); lockSeq = app_mouseSeq(); @@ -723,6 +759,8 @@ void waylandUncapturePointer(void) lockId = proxyId(wlWm.lockedPointer); zwp_locked_pointer_v1_destroy(wlWm.lockedPointer); wlWm.lockedPointer = NULL; + atomic_store_explicit(&wlWm.lockActive, false, + memory_order_release); lockSeq = app_mouseSeq(); } @@ -737,6 +775,8 @@ void waylandUncapturePointer(void) confDropId = internalUngrabPointer(false, &confDropSeq); else if (wlWm.pointer) { + atomic_store_explicit(&wlWm.confActive, false, + memory_order_release); wlWm.confinedPointer = createConfine(NULL); confReqId = proxyId(wlWm.confinedPointer); confReqSeq = app_mouseSeq(); @@ -748,6 +788,13 @@ void waylandUncapturePointer(void) 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) { if (wlWm.seat && diff --git a/client/displayservers/Wayland/wayland.c b/client/displayservers/Wayland/wayland.c index 500b4ffd..56563cb3 100644 --- a/client/displayservers/Wayland/wayland.c +++ b/client/displayservers/Wayland/wayland.c @@ -112,6 +112,8 @@ static bool waylandInit(const LG_DSInitParams params) LG_LOCK_INIT(wlWm.pendingHDRLock); LG_LOCK_INIT(wlWm.hdrLock); wlWm.desktop = WL_Desktops[0]; + atomic_init(&wlWm.confActive, false); + atomic_init(&wlWm.lockActive, false); atomic_init(&wlWm.cmFeaturesDone, false); atomic_init(&wlWm.cmCanDoHDR, false); atomic_init(&wlWm.hdrPQWhiteLevel, 203); @@ -321,6 +323,7 @@ struct LG_DisplayServerOps LGDS_Wayland = .ungrabPointer = waylandUngrabPointer, .capturePointer = waylandCapturePointer, .uncapturePointer = waylandUncapturePointer, + .isPointerCaptured = waylandIsPointerCaptured, .grabKeyboard = waylandGrabKeyboard, .ungrabKeyboard = waylandUngrabKeyboard, .getKeyLabel = waylandGetKeyLabel, diff --git a/client/displayservers/Wayland/wayland.h b/client/displayservers/Wayland/wayland.h index 35fae875..9fdec4fb 100644 --- a/client/displayservers/Wayland/wayland.h +++ b/client/displayservers/Wayland/wayland.h @@ -123,10 +123,11 @@ struct WaylandHDRParameters struct WaylandDSState { - bool pointerGrabbed; - bool keyboardGrabbed; - bool pointerInSurface; - bool focusedOnSurface; + _Atomic(bool) confActive; + _Atomic(bool) lockActive; + bool keyboardGrabbed; + bool pointerInSurface; + bool focusedOnSurface; WL_DesktopOps * desktop; @@ -378,6 +379,7 @@ void waylandUngrabKeyboard(void); void waylandUngrabPointer(void); void waylandCapturePointer(void); void waylandUncapturePointer(void); +bool waylandIsPointerCaptured(void); void waylandRealignPointer(void); void waylandWarpPointer(int x, int y, bool exiting); void waylandGuestPointerUpdated(double x, double y, double localX, double localY); diff --git a/client/displayservers/X11/x11.c b/client/displayservers/X11/x11.c index 433fc0eb..ad25ea52 100644 --- a/client/displayservers/X11/x11.c +++ b/client/displayservers/X11/x11.c @@ -1940,6 +1940,8 @@ static void x11GrabPointer(void) static void x11UngrabPointer(void) { + atomic_store_explicit(&x11.captureActive, false, memory_order_release); + if (!x11.pointerGrabbed) return; @@ -1952,10 +1954,14 @@ static void x11UngrabPointer(void) static void x11CapturePointer(void) { x11GrabPointer(); + atomic_store_explicit(&x11.captureActive, x11.pointerGrabbed, + memory_order_release); } 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: * - 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 @@ -1966,6 +1972,11 @@ static void x11UncapturePointer(void) x11UngrabPointer(); } +static bool x11IsPointerCaptured(void) +{ + return atomic_load_explicit(&x11.captureActive, memory_order_acquire); +} + static void x11GrabKeyboard(void) { if (x11.keyboardGrabbed) @@ -2143,6 +2154,7 @@ struct LG_DisplayServerOps LGDS_X11 = .ungrabPointer = x11UngrabPointer, .capturePointer = x11CapturePointer, .uncapturePointer = x11UncapturePointer, + .isPointerCaptured = x11IsPointerCaptured, .getKeyLabel = x11GetKeyLabel, .grabKeyboard = x11GrabKeyboard, .ungrabKeyboard = x11UngrabKeyboard, diff --git a/client/displayservers/X11/x11.h b/client/displayservers/X11/x11.h index 60b13c6a..e7edc1f4 100644 --- a/client/displayservers/X11/x11.h +++ b/client/displayservers/X11/x11.h @@ -93,8 +93,9 @@ struct X11DSState int xValuator; int yValuator; - bool pointerGrabbed; - bool keyboardGrabbed; + _Atomic(bool) captureActive; + bool pointerGrabbed; + bool keyboardGrabbed; bool entered; bool focused; bool fullscreen; diff --git a/client/include/interface/displayserver.h b/client/include/interface/displayserver.h index 83a026c7..abb83c04 100644 --- a/client/include/interface/displayserver.h +++ b/client/include/interface/displayserver.h @@ -210,6 +210,7 @@ struct LG_DisplayServerOps /* (un)capturePointer is used do toggle special cursor tracking in capture mode */ void (*capturePointer)(void); void (*uncapturePointer)(void); + bool (*isPointerCaptured)(void); /* get the active keymap's display label for the provided Linux keycode */ bool (*getKeyLabel)(int sc, char * label, size_t size); @@ -291,6 +292,7 @@ struct LG_DisplayServerOps DEBUG_ASSERT((x)->ungrabPointer ); \ DEBUG_ASSERT((x)->capturePointer ); \ DEBUG_ASSERT((x)->uncapturePointer ); \ + DEBUG_ASSERT((x)->isPointerCaptured ); \ DEBUG_ASSERT((x)->getKeyLabel ); \ DEBUG_ASSERT((x)->warpPointer ); \ DEBUG_ASSERT((x)->realignPointer ); \ diff --git a/client/src/core.c b/client/src/core.c index a5d5fac1..6bce64ae 100644 --- a/client/src/core.c +++ b/client/src/core.c @@ -530,9 +530,10 @@ void core_handleGuestMouseUpdate(void) void core_handleMouseGrabbed(double ex, double ey) { - const bool enabled = core_inputEnabled(); - MTRACE("captured delta=%.3f,%.3f enabled=%d", ex, ey, - enabled); + const bool active = g_cursor.grab && g_state.ds->isPointerCaptured(); + const bool enabled = active && core_inputEnabled(); + MTRACE("captured delta=%.3f,%.3f active=%d enabled=%d", ex, ey, + active, enabled); if (!enabled) return; diff --git a/client/tests/CMakeLists.txt b/client/tests/CMakeLists.txt index ff20f188..0ef7c5da 100644 --- a/client/tests/CMakeLists.txt +++ b/client/tests/CMakeLists.txt @@ -73,6 +73,7 @@ set(MOUSE_CASES capture-pending capture-revoked capture-release + capture-fallback rotate-scale geometry edges diff --git a/client/tests/mouse_test.c b/client/tests/mouse_test.c index 589fecd9..b4e5e83d 100644 --- a/client/tests/mouse_test.c +++ b/client/tests/mouse_test.c @@ -135,6 +135,12 @@ static void ungrab(void) static void capture(void) { + if (m.support == LG_DS_WARP_NONE) + { + grab(); + return; + } + m.conf.req = false; if (!m.conf.hold) m.conf.on = false; @@ -144,12 +150,23 @@ static void capture(void) static void uncapture(void) { + if (m.support == LG_DS_WARP_NONE) + { + ungrab(); + return; + } + push(EV_UNCAPTURE, 0, 0, false); m.lock.req = false; if (!m.lock.hold) 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) { push(EV_WARP, x, y, exiting); @@ -171,6 +188,7 @@ static struct LG_DisplayServerOps ds = { .ungrabPointer = ungrab, .capturePointer = capture, .uncapturePointer = uncapture, + .isPointerCaptured = captured, .warpPointer = warp, .isValidPointerPos = valid, }; @@ -407,6 +425,23 @@ static void testCapRelease(void) 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) { reset(); @@ -634,19 +669,20 @@ struct Test }; static const struct Test tests[] = { - { "inset-exit" , testInsetExit }, - { "exit-delay" , testExitWait }, - { "exit-guest" , testExitGuest }, - { "confine-pending", testConfWait }, - { "confine-guest" , testConfGuest }, - { "capture-pending", testCapWait }, - { "capture-revoked", testCapRevoke }, - { "capture-release", testCapRelease }, - { "rotate-scale" , testRotateScale }, - { "geometry" , testGeometry }, - { "edges" , testEdges }, - { "scales" , testScales }, - { "border-exit" , testBorderExit }, + { "inset-exit" , testInsetExit }, + { "exit-delay" , testExitWait }, + { "exit-guest" , testExitGuest }, + { "confine-pending" , testConfWait }, + { "confine-guest" , testConfGuest }, + { "capture-pending" , testCapWait }, + { "capture-revoked" , testCapRevoke }, + { "capture-release" , testCapRelease }, + { "capture-fallback", testCapFallback }, + { "rotate-scale" , testRotateScale }, + { "geometry" , testGeometry }, + { "edges" , testEdges }, + { "scales" , testScales }, + { "border-exit" , testBorderExit }, }; int main(int argc, char ** argv)