https://bugs.kde.org/show_bug.cgi?id=528162
Bug ID: 528162
Summary: Pointer lock: cursor position hint applied one
lock/unlock cycle late (6.8.x regression from 6.7.x)
Classification: Plasma
Product: kwin
Version First 6.8.2
Reported In:
Platform: CachyOS
OS: Linux
Status: REPORTED
Severity: major
Priority: NOR
Component: input
Assignee: [email protected]
Reporter: [email protected]
CC: [email protected]
Target Milestone: ---
Created attachment 197920
--> https://bugs.kde.org/attachment.cgi?id=197920&action=edit
The reproduction of the issue
DESCRIPTION
On Plasma 6.8.x, a client's zwp_locked_pointer_v1 cursor position hint
(set_cursor_position_hint) is applied one lock/unlock cycle late: when a
pointer
lock ends, kwin materializes the cursor at the position requested during the
PREVIOUS lock/unlock cycle, not the current one. Additionally, while a lock is
active the hidden pointer position is not held inert: it integrates the
relative
motion and reappears at window-bounds-jammed positions instead of where the
lock
was engaged. Both affect any SDL 3.2+ game that enters per-window relative
mouse
mode and repositions the cursor at unlock (SDL_WarpMouseInWindow; on
compositors
without wp_pointer_warp_v1 SDL issues the position hint). Games that don't
reposition silently show it as "cursor reappears at a screen edge after closing
a
menu / after camera panning".
STEPS TO REPRODUCE
1. Attach kwin_hint_lag_repro.c (self-contained SDL3 program, ~70 lines; builds
clean against SDL 3.4.0). Hold Right Mouse: the app enters per-window
relative
mode (locked pointer) and hides the cursor.
2. Move the mouse around, then release RMB. The app requests the cursor at the
next window corner (the request and every compositor-reported motion
position
are printed), exits relative mode, and shows the cursor.
3. Repeat several times so the requested corner cycles. Console shows each
requested target vs. where kwin actually reported the cursor afterwards.
OBSERVED RESULT
The cursor appears at the corner requested on the PREVIOUS release, every
cycle.
Recorded sequence from a game session on my machine (window-local coordinates,
1920x1009 window; "requested" = warp request recorded at that unlock,
"reported" =
first compositor-delivered absolute motion after that unlock):
release 2: requested (871,442) -> reported (1226,448) = release 1's
request
release 3: requested (1670,374) -> reported (871,442) = release 2's
request
release 4: requested (1065,410) -> reported (1670,374) = release 3's
request
release 5: requested (1425,526) -> reported (1065,410) = release 4's
request
Five for five in that session; 65 of 74 recorded releases across sessions show
the
same one-cycle lag (the fresh ones are rapid re-locks at the same spot). Rapid
manual lock/unlock repetition converges the cursor onto the requests - each
forced
locked->unlocked transition seems to consume exactly one outstanding hint.
The drift observation: with no hint at all, after long relative-mode pans the
cursor reappears at bounds-jammed positions (logged first post-unlock absolutes
of
x=0 on a 1920-wide window), at a distance correlating with the pan deltas
rather
than with any hand movement after the unlock.
EXPECTED RESULT
Per pointer-constraints-unstable-v1, a locked pointer's position is inert for
the
lock's duration, and a position hint set on a locked pointer determines where
the
pointer reappears when the lock deactivates - the request from the SAME unlock
should land at that unlock. On kwin 6.7.4 the same game binary showed no unlock
displacement over days of play immediately before the upgrade; the symptom
appeared right after the update (pacman log: kwin 6.7.4-7.1 -> 6.8.0-1.1,
2026-10-17), with no change to the client.
SOFTWARE/OS VERSIONS
Operating System: CachyOS Linux
KDE Plasma Version: 6.8.2
KDE Frameworks Version: 6.31.0
Qt Version: 6.12.0
Kernel Version: 7.3.1-1-cachyos (64-bit)
Graphics Platform: Wayland
Graphics Processor: NVIDIA GeForce RTX 4070 (driver 615.78.08)
kwin: 6.8.2-1.1
ADDITIONAL INFORMATION
Possible mechanism, from reading the 6.8.2 source (offering as pointers, not a
diagnosis):
1. src/wayland/surface.cpp, SurfaceState::mergeInto(): the hint copy
target->pointerLockHint = pointerLockHint;
runs WITHOUT the `committed & SurfaceState::Field::PointerLockHint` guard
that
protects the pointerLockRegion copy directly above it, so every state
amendment re-assigns the hint field.
2. src/wayland/pointerconstraints_v1.cpp, ~LockedPointerV1Interface(): the
unlock
amendment notes "the position hint is intentionally not reset, since it will
be
used when the next commit is applied". Combined with the unguarded merge,
the
unlock amendment appears to overwrite a freshly committed hint (sent in the
same cycle) with the older value it carries.
3. Application happens in the locked->unlocked transition in
PointerInputRedirection::updatePointerConstraints (src/pointer_input.cpp),
which reads SurfaceInterface::lockedPointerHint() - the current, stale
value.
The one-exchange-late signature and the convergence-by-repetition fall out of
that model naturally: each transition consumes the outstanding (older) hint,
and
forcing repeated transitions flushes them - which is the workaround my game
client currently ships (we drive a few rapid lock->hint->unlock exchanges at
unlock while the cursor is still hidden until the position converges; reliable,
but it generates compositor churn, and rapid user re-locks then run into lock
re-entry throttling).
SDL context: SDL 3.x implements WarpMouseInWindow on hintless compositors via a
short-lived locked pointer carrying set_cursor_position_hint before the unlock;
SDL 2.x never sends the hint (pure internal bookkeeping), so older games only
show the drift symptom, which is likely why this reads as underreported.
--
You are receiving this mail because:
You are watching all bug changes.