[client] X11: release pointer after capture

Normal input now uses absolute positioning, so retaining the XI pointer
grab after capture leaves no later path to release it.

Always release the grab so leaving the window works again and the
uncapture path can align the host cursor to the guest position.
This commit is contained in:
Geoffrey McRae
2026-08-10 18:47:27 +10:00
parent 4afb0254d1
commit 89dcf56073
3 changed files with 51 additions and 11 deletions

View File

@@ -2103,19 +2103,9 @@ static void x11CapturePointer(void)
static void x11UncapturePointer(void)
{
const bool ungrab = !app_isFormatValid() || app_isCaptureOnlyMode();
LG_LOCK(x11.pointerLock);
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
* window when we release it.
* - if the user has opted to use captureInputOnly mode.
*/
if (ungrab)
x11UngrabPointerLocked();
x11UngrabPointerLocked();
LG_UNLOCK(x11.pointerLock);
}

View File

@@ -126,6 +126,7 @@ set(MOUSE_CASES
capture-revoked
capture-release
capture-align
x11-capture-exit
capture-fallback
rotate-scale
geometry

View File

@@ -67,6 +67,7 @@ static struct
struct Trap conf;
struct Trap lock;
bool valid;
bool captureGrab;
struct Ev ev[256];
unsigned int count;
}
@@ -152,6 +153,18 @@ static void capture(void)
return;
}
if (m.captureGrab)
{
m.conf.req = true;
if (!m.conf.hold)
m.conf.on = true;
m.lock.req = true;
if (!m.lock.hold)
m.lock.on = m.conf.on;
push(EV_CAPTURE, 0, 0, false);
return;
}
m.conf.req = false;
if (!m.conf.hold)
m.conf.on = false;
@@ -171,6 +184,9 @@ static void uncapture(void)
m.lock.req = false;
if (!m.lock.hold)
m.lock.on = false;
if (m.captureGrab)
ungrab();
}
static bool captured(void)
@@ -592,6 +608,38 @@ static void testCapAlign(void)
CHECK(!m.ev[warp].exit);
}
static void testX11CapExit(void)
{
reset();
setLocal(50, 50);
m.support = LG_DS_WARP_SCREEN;
m.captureGrab = true;
m.conf.req = false;
m.conf.on = false;
core_setGrabQuiet(true);
CHECK(m.conf.on);
CHECK(m.lock.on);
g_cursor.guest.x += 10;
m.count = 0;
core_setGrabQuiet(false);
const int uncapture = first(EV_UNCAPTURE);
const int ungrab = first(EV_UNGRAB);
const int warp = first(EV_WARP);
CHECK(uncapture >= 0);
CHECK(ungrab > uncapture);
CHECK(warp > ungrab);
CHECK(m.ev[warp].x == 60);
CHECK(m.ev[warp].y == 50);
CHECK(!m.ev[warp].exit);
CHECK(!m.conf.req);
CHECK(!m.conf.on);
CHECK(!m.lock.req);
CHECK(!m.lock.on);
}
static void testCapFallback(void)
{
reset();
@@ -853,6 +901,7 @@ static const struct Test tests[] = {
{ "capture-revoked" , testCapRevoke },
{ "capture-release" , testCapRelease },
{ "capture-align" , testCapAlign },
{ "x11-capture-exit", testX11CapExit },
{ "capture-fallback", testCapFallback },
{ "rotate-scale" , testRotateScale },
{ "geometry" , testGeometry },