Repository navigation
Fix nested cursor offset and cursor vanishing at top/left edges - #2519
Open
danjedwards123 wants to merge 2 commits into
Open
danjedwards123 wants to merge 2 commits into
danjedwards123 wants to merge 2 commits into
Conversation
12706c3 stopped applying the cursor hotspot and --cursor-scale-height to the composited cursor layer whenever the mouse connector has nested hints, because OpenVR's cursor overlay applies the hotspot itself. The nested Wayland and SDL backends also have nested hints, but they draw the composited cursor layer as-is, e.g. when --force-grab-cursor makes gamescope composite the cursor. There the cursor image is now drawn offset down-right of the real pointer position by the hotspot, so clicks land up-left of where the cursor appears. Add INestedHints::CursorLayerIsHotspotPoint(), true only for OpenVR, and use it for both the hotspot offset and the cursor texture sizing.
The subsurface position was converted to logical coordinates with WaylandScaleToLogical(), which works on uint32_t. A plane positioned above or left of the output, such as the cursor near the top or left edge once it is offset by its hotspot, wrapped around to a position tens of millions of pixels away and was not shown at all. Convert positions with a signed helper that rounds the same way for positive values.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
1. Issues seen in Final Fantasy XIV
Running Final Fantasy XIV (via XIVLauncher) in nested gamescope on Arch with
--force-grab-cursor -f -W 2560 -H 1440 -r 240 -s 1.7:Clicks don't line up with the cursor. Every click lands up and to the left of
where the cursor is drawn, so menu buttons have to be clicked below and to the right
of where they visually appear. Present from 3.16.29 onwards, including 3.17.0.
3.16.28 and earlier are unaffected.
Screenshot below demonstrating this issue on 3.17.0. Note how the cursor has to be quite far down and to the right to select the 'Start' button:
The cursor disappears near the top and left edges. Within a small band along the
top and left of the screen, the cursor is not drawn at all, although clicks there still
register. Present in 3.16.28 and earlier. From 3.16.29 it is hidden by issue 1, and it
comes back once issue 1 is fixed.
The band across the top and down the left where the cursor no longer becomes visible matches the size of the offset to mouse clicks introduced in 3.17.0.
Screenshot below demonstrating this issue on 3.16.28. Here my mouse is hovered over the 'System Configuration' menu item. But when the cursor is close to the left, it isn't visible. But when moved to the right, the cursor becomes visible again, as shown in the second image.
2. Logic errors that caused them
MouseCursor::paint()andMouseCursor::getTexture()now skip the cursor hotspot offsetand
--cursor-scale-heightsizing whenever the mouse connector hasINestedHints. That'scorrect for OpenVR, whose cursor overlay takes the hotspot from
CursorInfoviaSetOverlayTransformCursor. But the Wayland and SDL backends also exposeINestedHints,and they draw the composited cursor layer as-is. That happens whenever gamescope
composites the cursor itself, which
--force-grab-cursorforces throughg_bForceRelativeMouseinShouldDrawCursor(). On those backends the cursor image isplaced with its top-left corner at the pointer position instead of its hotspot.
CWaylandPlane::Present()converts the subsurface position withWaylandScaleToLogical(),which takes and returns
uint32_t. When the cursor is offset by its hotspot, its positiongoes negative near the top and left edges. A position of -1 at scale 120 becomes
35791394, so the subsurface is placed far off screen. 12706c3 hid this by no longer
applying the hotspot, which is why it only shows on 3.16.28 and earlier, or once fix 1
is applied.
3. The fix
steamcompmgr: only skip the cursor hotspot for connectors that apply itAdds
INestedHints::CursorLayerIsHotspotPoint(), defaulting tofalseand overridden totrueinCOpenVRConnector. A helper,cursor_layer_is_hotspot_point(), replaces both"has nested hints" checks in
MouseCursor.The original check asked the wrong question: "is this a nested backend?" when what
matters is "does this backend apply the hotspot itself?". Only OpenVR does. The new
method asks that directly, so each backend declares its own behaviour. OpenVR keeps
what 12706c3 introduced, and Wayland and SDL return to the pre-3.16.29 behaviour,
where gamescope moves the image up and left by the hotspot so its tip sits on the
click point. Any future backend defaults to gamescope applying the hotspot, matching
DRM.
WaylandBackend: keep negative plane positions negativeAdds
WaylandScalePositionToLogical()and uses it forwl_subsurface_set_position().Sizes still use the unsigned
WaylandScaleToLogical(), since they can't be negative.The new helper multiplies in signed 64-bit, so a negative position stays negative and
can't overflow. It rounds towards +∞, giving identical results to
WaylandScaleToLogical()for every positive value, so nothing changes away from theedges. Near the top and left edges, the cursor subsurface now sits just past the edge,
as intended, and the host compositor shows the part that's on screen.
4. Testing
Bisect
12706c3^+f3b792a12706c3+f3b792a3.16.29, and any commit between bd52d6e and its revert f3b792a, can't launch the game under
Proton/umu: setting
WAYLAND_DISPLAYto an empty string makes pressure-vessel warn and thelaunch chain crash on a D-Bus assertion. To test
12706c3and its parent directly, Iapplied
f3b792a(main.cpp only, unrelated to the cursor) on top of each. The parent hasno offset and
12706c3does, so 12706c3 introduces the offset.With 3.17.0 + this PR, it can be seen in the screenshots below that both the click offset is eliminated and the cursor no longer disappears when close to the top or left of the screen:


Steps to reproduce and verify
Both issues need gamescope to draw the cursor itself (nested Wayland backend with
--force-grab-cursor) and a client cursor whose hotspot isn't at its top-left corner.Final Fantasy XIV's system cursor meets both.
--force-grab-cursor -f -W 2560 -H 1440 -r 240 -s 1.7.the cursor's tip.
clicked below and to their right.
reaching the edge, though clicks there still register.
Tested on: Arch, Niri, NVIDIA RTX 4070, GE-Proton11-6 via
umu-launcher, Final Fantasy XIV.
AI-assisted: This PR was prepared with help from an AI assistant (Claude), which helped trace the regression through the source, write both patches and draft this description. I reviewed the changes, built them, and did all of the testing and bisecting described above on my own hardware.