Skip to content

X11: cache raw mouse valuators by source device to fix "mouse stuck in middle" - #16259

Open
edmundselliot wants to merge 1 commit into
libsdl-org:mainfrom
edmundselliot:fix/x11-source-device-cache
Open

X11: cache raw mouse valuators by source device to fix "mouse stuck in middle"#16259
edmundselliot wants to merge 1 commit into
libsdl-org:mainfrom
edmundselliot:fix/x11-source-device-cache

Conversation

@edmundselliot

@edmundselliot edmundselliot commented Sep 7, 2026

Copy link
Copy Markdown
  • I confirm that I am the author of this code and release it to the SDL project under the zlib license. This contribution does not contain code from other sources, including code generated by a Large Language Model ("AI").

Description

Use the raw event's sourceid when querying and caching its XInput2 valuator metadata. An XWayland master pointer can deliver events from both an absolute pointer source and a relative pointer source. Caching metadata by the shared master deviceid lets the relative source inherit an absolute-axis classification. SDL then subtracts consecutive values that are already relative movements, reversing mouse-look movement as the user slows the mouse.

The source-specific lookup gives each source its own axis classification and previous-coordinate state. This remains a candidate correction pending maintainer review and multi-day gameplay testing.

Validation

Observed in a running native CS2 session on GNOME Wayland using the SDL X11 backend:

  • Incoming XInput events had deviceid=2, sourceid=7; source 7 advertised relative X/Y axes.
  • Probes at the installed SDL library's actual classification branch found its cached device 2 entry had relative[0]=0 and relative[1]=0.
  • Across 218 samples per axis, the absolute-conversion branch ran 436 times; the relative passthrough branch never ran.
  • Same-process boundary tracing showed correct XInput raw values becoming consecutive differences in the SDL events returned to CS2.

An isolated helper loaded the game's original SDL library with the dummy video backend, reconstructed the observed master-absolute/source-relative cache state, and fed synthetic raw events through its compiled X11 handler. A source-equivalent, one-byte local binary correction was assembled and tested against the exact same library:

Existing Issue(s)

Related to #16163. The live branch trace confirms the absolute-conversion path requested in that issue; the exact first-event ordering on this machine was not captured.

@edmundselliot
edmundselliot marked this pull request as ready for review September 7, 2026 02:58
@edmundselliot edmundselliot changed the title X11: cache raw mouse valuators by source device X11: cache raw mouse valuators by source device to fix "mouse stuck in middle" Sep 7, 2026
@edmundselliot

edmundselliot commented Sep 7, 2026

Copy link
Copy Markdown
Author

Hi - this issue was affecting cs2 mouse tracking, where in game the mouse could effectively get stuck in the middle of the screen (seems like a common issue others are hitting according to ValveSoftware/csgo-osx-linux#4354), fixed by restarting the game and "getting lucky".

I debugged it using an AI tool, but I reviewed the debug/code change, and the fix makes sense.

Because the bad classification is cached during startup, the initial input sequence can vary, so this only reproduced about ~25% of the time for me. With the fix I started my game O(20) times and it worked every time!

@edmundselliot

edmundselliot commented Sep 7, 2026

Copy link
Copy Markdown
Author

Seems pretty much the same issue as #12968

@edmundselliot

Copy link
Copy Markdown
Author

@Kontrabant @icculus @slouken please let me know there's anything else I should do for this fix, haven't contributed here before so not sure of the process 😄

@Kontrabant

Copy link
Copy Markdown
Contributor

This fix is strange. It's applying the relative pointer slave device valuator mappings to events from the master device. If this fixes things, then there is a mismatch and the master pointer is incorrectly reporting relative axes as absolute. Why is this happening though?

@edmundselliot

Copy link
Copy Markdown
Author

Yeah, this is the part that confused me too.

In the failing case, XI_RawMotion is delivered as deviceid=2, sourceid=7. Device 2 reports its X/Y valuators as absolute, while source 7 (xwayland-relative-pointer) reports them as relative. But the raw values in those events behave as relative deltas, not absolute coordinates.

I verified this by tracing the XI2 values before SDL processes them. When SDL uses device 2’s absolute classification, parse_relative_valuators() subtracts consecutive raw values and the resulting SDL motion is exactly delta-of-delta. Using source 7’s relative classification passes those same values through unchanged and fixes the motion.

So I agree there seems to be a mismatch between the master device’s advertised valuator classes and the semantics of these raw events. I’m not yet sure whether that’s expected XI2/XWayland behavior?

@edmundselliot

Copy link
Copy Markdown
Author

I dug into the XWayland/XI2 side, and I think this explains why the master can report absolute axes here.

XWayland creates two slave devices attached to the same master: xwayland-pointer with absolute X/Y valuators, and xwayland-relative-pointer with relative X/Y valuators. Relative-pointer motion is queued on the latter using POINTER_RAWONLY.

The master’s device classes can change when the active slave changes. The server copies the active slave’s classes to the master and sends an XI_DeviceChanged event with SlaveSwitch.

So the master can have absolute classes after the absolute XWayland slave was active, while a later XI_RawMotion event contains raw values from the relative slave.

SDL currently caches the device info by deviceid, so those master classes can stick around after the source changes. That can make SDL interpret the raw values incorrectly.

I think the issue is that the master’s classes are being treated as stable, even though they can change depending on which slave was active.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants