mirror of
https://github.com/gnif/LookingGlass.git
synced 2026-08-09 08:41:31 +00:00
[client] cursor: restore Wayland edge synchronization
Keep guest-to-host pointer synchronization active while an attempted surface exit is blocked by the edge of a Wayland output. Continue forwarding relative motion and wait for wl_pointer.leave before marking the cursor outside the window. Distinguish whole-surface releases from exits into letterbox space so the expected asynchronous release callback cannot disable the feedback loop before the pointer is confined again. Treat Wayland pointer capability loss as a real leave so an unrelated inactive callback cannot be mistaken for the intentional release.
This commit is contained in:
@@ -553,8 +553,11 @@ static void waylandCleanUpPointer(bool notify)
|
|||||||
MLOG(confSeq, "conf destroy id=%u why=pointer", confId);
|
MLOG(confSeq, "conf destroy id=%u why=pointer", confId);
|
||||||
|
|
||||||
if (event)
|
if (event)
|
||||||
|
{
|
||||||
|
app_handleEnterEvent(false);
|
||||||
app_handleGrabEvent(false);
|
app_handleGrabEvent(false);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Seat-handling listeners.
|
// Seat-handling listeners.
|
||||||
|
|
||||||
|
|||||||
@@ -70,6 +70,16 @@ static void cancelExit(const char * why)
|
|||||||
g_cursor.exit = false;
|
g_cursor.exit = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void cancelSurfaceExit(const char * why)
|
||||||
|
{
|
||||||
|
if (!g_cursor.surfaceExit)
|
||||||
|
return;
|
||||||
|
|
||||||
|
MTRACE("surface exit cancel=%s", why);
|
||||||
|
g_cursor.surfaceExit = false;
|
||||||
|
g_cursor.warpState = WARP_STATE_ON;
|
||||||
|
}
|
||||||
|
|
||||||
static void finishExit(void)
|
static void finishExit(void)
|
||||||
{
|
{
|
||||||
if (!g_cursor.exit || g_cursor.viewReq)
|
if (!g_cursor.exit || g_cursor.viewReq)
|
||||||
@@ -193,6 +203,9 @@ void core_setCursorInView(bool enable)
|
|||||||
enable, g_cursor.viewReq, g_cursor.inView, g_state.focused,
|
enable, g_cursor.viewReq, g_cursor.inView, g_state.focused,
|
||||||
g_cursor.inWindow, g_cursor.grab);
|
g_cursor.inWindow, g_cursor.grab);
|
||||||
|
|
||||||
|
if (!enable)
|
||||||
|
cancelSurfaceExit("view");
|
||||||
|
|
||||||
if (g_cursor.exit &&
|
if (g_cursor.exit &&
|
||||||
(enable || !g_state.focused || !g_cursor.inWindow ||
|
(enable || !g_state.focused || !g_cursor.inWindow ||
|
||||||
g_state.ignoreInput || !g_state.posInfoValid || app_isOverlayMode()))
|
g_state.ignoreInput || !g_state.posInfoValid || app_isOverlayMode()))
|
||||||
@@ -253,6 +266,17 @@ void core_handleGrabEvent(bool active)
|
|||||||
warpSupport == LG_DS_WARP_NONE)
|
warpSupport == LG_DS_WARP_NONE)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
if (g_cursor.surfaceExit)
|
||||||
|
{
|
||||||
|
if (!active && g_cursor.viewReq && g_cursor.inWindow)
|
||||||
|
{
|
||||||
|
MTRACE("surface exit release pending");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
cancelSurfaceExit(active ? "confined" : "state");
|
||||||
|
}
|
||||||
|
|
||||||
if (!active)
|
if (!active)
|
||||||
finishExit();
|
finishExit();
|
||||||
|
|
||||||
@@ -282,6 +306,7 @@ void core_setGrabQuiet(bool enable)
|
|||||||
if (g_cursor.grab == enable)
|
if (g_cursor.grab == enable)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
cancelSurfaceExit("capture");
|
||||||
g_cursor.grab = enable;
|
g_cursor.grab = enable;
|
||||||
g_cursor.acc.x = 0.0;
|
g_cursor.acc.x = 0.0;
|
||||||
g_cursor.acc.y = 0.0;
|
g_cursor.acc.y = 0.0;
|
||||||
@@ -541,6 +566,20 @@ void core_updatePositionInfo(void)
|
|||||||
}
|
}
|
||||||
|
|
||||||
done:
|
done:
|
||||||
|
if (g_cursor.surfaceExit)
|
||||||
|
{
|
||||||
|
if (g_cursor.viewReq && g_cursor.inWindow && g_state.focused &&
|
||||||
|
!g_state.ignoreInput && g_state.posInfoValid &&
|
||||||
|
!app_isOverlayMode())
|
||||||
|
{
|
||||||
|
MTRACE("surface exit regrab=geometry");
|
||||||
|
g_cursor.warpState = WARP_STATE_ON;
|
||||||
|
g_state.ds->grabPointer();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
core_setCursorInView(false);
|
||||||
|
}
|
||||||
|
|
||||||
MTRACE("geometry src=%dx%d dst=%d,%d,%d,%d win=%dx%d "
|
MTRACE("geometry src=%dx%d dst=%d,%d,%d,%d win=%dx%d "
|
||||||
"scale=%.4f,%.4f uiScale=%.4f rot=%d/%d border=%d,%d,%d,%d "
|
"scale=%.4f,%.4f uiScale=%.4f rot=%d/%d border=%d,%d,%d,%d "
|
||||||
"valid=%d", g_state.srcSize.x, g_state.srcSize.y,
|
"valid=%d", g_state.srcSize.x, g_state.srcSize.y,
|
||||||
@@ -885,6 +924,15 @@ fallback:
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case LG_DS_WARP_SURFACE:
|
case LG_DS_WARP_SURFACE:
|
||||||
|
if (tx < 0 || tx >= g_state.windowW ||
|
||||||
|
ty < 0 || ty >= g_state.windowH)
|
||||||
|
{
|
||||||
|
g_cursor.surfaceExit = true;
|
||||||
|
g_state.ds->ungrabPointer();
|
||||||
|
core_warpPointer(tx, ty, true);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
startExit(local.x, local.y);
|
startExit(local.x, local.y);
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
|||||||
@@ -147,6 +147,7 @@ static void lgInit(void)
|
|||||||
g_cursor.inView = false;
|
g_cursor.inView = false;
|
||||||
g_cursor.viewReq = false;
|
g_cursor.viewReq = false;
|
||||||
g_cursor.exit = false;
|
g_cursor.exit = false;
|
||||||
|
g_cursor.surfaceExit = false;
|
||||||
g_cursor.guest.valid = false;
|
g_cursor.guest.valid = false;
|
||||||
|
|
||||||
// if spice is not in use, hide the local cursor
|
// if spice is not in use, hide the local cursor
|
||||||
|
|||||||
@@ -301,6 +301,9 @@ struct CursorState
|
|||||||
/* true if a pointer exit is waiting for confinement to release */
|
/* true if a pointer exit is waiting for confinement to release */
|
||||||
bool exit;
|
bool exit;
|
||||||
|
|
||||||
|
/* true if a Wayland surface exit is waiting for a real leave */
|
||||||
|
bool surfaceExit;
|
||||||
|
|
||||||
/* the local pointer exit target */
|
/* the local pointer exit target */
|
||||||
struct DoublePoint exitPos;
|
struct DoublePoint exitPos;
|
||||||
|
|
||||||
|
|||||||
@@ -66,6 +66,7 @@ target_link_libraries(mouse-tests
|
|||||||
)
|
)
|
||||||
set(MOUSE_CASES
|
set(MOUSE_CASES
|
||||||
inset-exit
|
inset-exit
|
||||||
|
surface-exit
|
||||||
exit-delay
|
exit-delay
|
||||||
exit-guest
|
exit-guest
|
||||||
exit-cancel
|
exit-cancel
|
||||||
|
|||||||
@@ -329,6 +329,83 @@ static void setConf(bool active)
|
|||||||
core_handleGrabEvent(active);
|
core_handleGrabEvent(active);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void setSurfaceEdge(double x, double y)
|
||||||
|
{
|
||||||
|
reset();
|
||||||
|
g_state.windowW = 100;
|
||||||
|
g_state.windowH = 80;
|
||||||
|
g_state.dstRect = (LG_RendererRect) {
|
||||||
|
.valid = true,
|
||||||
|
.x = 0,
|
||||||
|
.y = 0,
|
||||||
|
.w = 100,
|
||||||
|
.h = 80,
|
||||||
|
};
|
||||||
|
setLocal(x, y);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void testSurfaceExit(void)
|
||||||
|
{
|
||||||
|
setSurfaceEdge(99, 40);
|
||||||
|
|
||||||
|
core_handleMouseNormal(1, 0);
|
||||||
|
|
||||||
|
const int drop = first(EV_UNGRAB);
|
||||||
|
const int warp = first(EV_WARP);
|
||||||
|
const int move = first(EV_MOTION);
|
||||||
|
CHECK(drop >= 0);
|
||||||
|
CHECK(warp > drop);
|
||||||
|
CHECK(move > warp);
|
||||||
|
CHECK(m.ev[warp].x == 100);
|
||||||
|
CHECK(m.ev[warp].y == 40);
|
||||||
|
CHECK(m.ev[move].x == 1);
|
||||||
|
CHECK(m.ev[move].y == 0);
|
||||||
|
CHECK(g_cursor.surfaceExit);
|
||||||
|
CHECK(g_cursor.inWindow);
|
||||||
|
CHECK(g_cursor.inView);
|
||||||
|
CHECK(g_cursor.viewReq);
|
||||||
|
|
||||||
|
core_handleMouseNormal(1, 0);
|
||||||
|
CHECK(count(EV_MOTION) == 2);
|
||||||
|
CHECK(g_cursor.surfaceExit);
|
||||||
|
CHECK(g_cursor.inWindow);
|
||||||
|
CHECK(g_cursor.inView);
|
||||||
|
CHECK(g_cursor.viewReq);
|
||||||
|
|
||||||
|
core_handleGuestMouseUpdate();
|
||||||
|
CHECK(count(EV_GUEST) == 1);
|
||||||
|
|
||||||
|
setLocal(99, 40);
|
||||||
|
core_handleMouseNormal(-1, 0);
|
||||||
|
CHECK(m.conf.req);
|
||||||
|
CHECK(g_cursor.surfaceExit);
|
||||||
|
|
||||||
|
setConf(false);
|
||||||
|
CHECK(g_cursor.inView);
|
||||||
|
CHECK(g_cursor.surfaceExit);
|
||||||
|
|
||||||
|
setConf(true);
|
||||||
|
CHECK(g_cursor.inView);
|
||||||
|
CHECK(!g_cursor.surfaceExit);
|
||||||
|
|
||||||
|
setSurfaceEdge(0, 79);
|
||||||
|
core_handleMouseNormal(-1, 1);
|
||||||
|
CHECK(g_cursor.surfaceExit);
|
||||||
|
CHECK(g_cursor.inWindow);
|
||||||
|
CHECK(g_cursor.inView);
|
||||||
|
CHECK(g_cursor.viewReq);
|
||||||
|
CHECK(count(EV_MOTION) == 1);
|
||||||
|
|
||||||
|
core_handleGuestMouseUpdate();
|
||||||
|
CHECK(count(EV_GUEST) == 1);
|
||||||
|
|
||||||
|
g_cursor.inWindow = false;
|
||||||
|
core_setCursorInView(false);
|
||||||
|
CHECK(!g_cursor.surfaceExit);
|
||||||
|
CHECK(!g_cursor.inView);
|
||||||
|
CHECK(!g_cursor.viewReq);
|
||||||
|
}
|
||||||
|
|
||||||
static void startExit(void)
|
static void startExit(void)
|
||||||
{
|
{
|
||||||
reset();
|
reset();
|
||||||
@@ -742,6 +819,7 @@ struct Test
|
|||||||
|
|
||||||
static const struct Test tests[] = {
|
static const struct Test tests[] = {
|
||||||
{ "inset-exit" , testInsetExit },
|
{ "inset-exit" , testInsetExit },
|
||||||
|
{ "surface-exit" , testSurfaceExit },
|
||||||
{ "exit-delay" , testExitWait },
|
{ "exit-delay" , testExitWait },
|
||||||
{ "exit-guest" , testExitGuest },
|
{ "exit-guest" , testExitGuest },
|
||||||
{ "exit-cancel" , testExitCancel },
|
{ "exit-cancel" , testExitCancel },
|
||||||
|
|||||||
Reference in New Issue
Block a user