SDL
SDL copied to clipboard
add alternate raw mouse motion events with windows implementation
Proof of concept that closes #7674. Other platforms can be implemented on a rolling basis but shouldn't be a blocker for merge.
So thank you for the proof of concept. I won't accept this as-is, because it would need some style cleanup and additional work, but this clearly demonstrates what you're going for.
Since it's been a while, can you remind me of the use cases for this API?
I originally wanted this in the context of having raw inputs being mapped to sdl codes across platforms. So raw mouse and keyboard would have their corresponding codes I can reuse across platforms. Maybe if sdl exposes some internal methods for the conversion between win32, evdev, etc tables or such
The primary motivation for this is the ability to receive raw device counts while staying in non-relative mode.
An example use-case is a twitch-action MOBA demanding low-latency hardware cursor (ruling out simulating a relativemode cursor) where the cursor is constrained inside the window client area, and you want to be able to pan the camera based on mouse displacement by moving the cursor to the edge of the screen (as opposed to the traditional way of moving the camera at a fixed rate when the game detects that the cursor hits the edge of the window)
Also, I'd like to use this PR as an opportunity to properly learn how SDL's is structured (the current impl was intended to be a throwaway sketch anyways), so if you can give me some leads as to which part of the codebase I should first look into to implement such functionality in a non-shoehorned way, it would be greatly speed up my familiarization.
The primary motivation for this is the ability to receive raw device counts while staying in non-relative mode.
An example use-case is a twitch-action MOBA demanding low-latency hardware cursor (ruling out simulating a relativemode cursor) where the cursor is constrained inside the window client area, and you want to be able to pan the camera based on mouse displacement by moving the cursor to the edge of the screen (as opposed to the traditional way of moving the camera at a fixed rate when the game detects that the cursor hits the edge of the window)
Yup, this makes sense.
Also, I'd like to use this PR as an opportunity to properly learn how SDL's is structured (the current impl was intended to be a throwaway sketch anyways), so if you can give me some leads as to which part of the codebase I should first look into to implement such functionality in a non-shoehorned way, it would be greatly speed up my familiarization.
Sure, I'll add some comments to show what I would change.
Yes, this looks good. If this functionality is merged, would you still need SDL_HINT_MOUSE_RELATIVE_CURSOR_VISIBLE and SDL_HINT_MOUSE_RELATIVE_CLIP_INTERVAL?
For SDL2, both are still required as workaround since it won’t have the new rawinput subsystem.
For SDL3, I’d still like to have the option of manual cursor visibility management in cases where I sometime still need to fall back to using relative mode -- such as dynamically varying SDL’s mouse scaling based on asynchronous state -- implementing from pure raw motion channel would entail rolling my own signalling and callback logic, when SDL already provides it for free. But strictly speaking yes the raw motion channel provides enough of an escape hatch for me to live without that knob.
Manual cursor confinement management is also a nice-to-have QoL, though working around its absence isn’t as much of an inconvenience as lacking manual cursor visibility management. Though the other way of using that hint -- making the refresh more frequent -- may be a use case that others might find useful, such as circumventing interference from other applications.
Beyond that, both of those also have minor value as a debugging knob.
Ultimately though, having the raw motion channel as the ultimate escape hatch makes those two hints less of a pressing need, opening up breathing room to potentially refactor them into a more cohesive form that is supplementary as opposed to a stopgap shoehorn.
I should note that I haven’t directly tested the PR myself, since I haven’t yet figured out how to drop it into Love2D 11.x, so I don’t know if it currently has any unexpected interactions with the relative mode state machine
I'm inclined to accept this PR. It allows the application more flexibility listening to raw mouse deltas and is the only way for it to get them when SDL has raw input enabled on Windows, because there can be only one raw input listener at a time.
@icculus, thoughts?
Also I should probably add a button event counterpart to the raw channel, since mixing WM_MOUSE* button events with an asynchronous thread will result in out-of-sync ordering of button presses and movement. I didn’t add it earlier because I expected to still use relative mode for the main motion, raw movement being supplementary for the occasional interactions, but if the two hints are removed then a raw-channel-only implementation must also read the raw button pressses to get the correct ordering of events.
Exposing the raw data like this is a good idea, I think. Is this meant to be Windows only, or can we wire this up for things like Xinput2 on X11, etc?
(If so, we can do that in later commits, and not hold up this PR.)
Exposing the raw data like this is a good idea, I think. Is this meant to be Windows only, or can we wire this up for things like Xinput2 on X11, etc?
We can do this for other platforms too.
Also I should probably add a button event counterpart to the raw channel, since mixing WM_MOUSE* button events with an asynchronous thread will result in out-of-sync ordering of button presses and movement. I didn’t add it earlier because I expected to still use relative mode for the main motion, raw movement being supplementary for the occasional interactions, but if the two hints are removed then a raw-channel-only implementation must also read the raw button pressses to get the correct ordering of events.
Good point.
The raw wheel event needs some reference point for client applications to interpret it, as different platforms can report this value in many ways. Win32 and Wayland with high-resolution scroll wheel support use a system where 120 units equals 1 whole scroll wheel tick, Wayland without high-resolution scroll support uses 10 units per tick, on X11 1 unit equals 1 tick, and Cocoa sends wheel deltas as floats.
Two solutions are:
- Use a float instead of an integer value
- Specify that the integer delta is a numerator that needs to be divided by some denominator to interpret the fractional delta (120 seems to be commonly used)
If we are passing through the native data, then we should probably use floats. The GCMouseInput deltas definitely have sub-pixel precision, and if we want to adhere to your reasoning, we should pass that through as-is.
@Kontrabant I have implemented a hybrid approach:
- raw axis readings are supplied as int
- associated denominators for each axis are supplied as float
This way, platforms with floating point counts can be represented with the 23 mantissa bits as numerator integer and the denominator being signed negative powers of two, while platforms with integer counts can have the numerator as-is while the denominator is just a float value of 1.0.
Logically it also makes sense in terms of being a raw "axis" event, i.e. hardwares in general report a logical numeric value relative to some arbitrary denomination size, which is then calibrated to report a normalized "scale bar" of real units.
This also potentially opens up for future possibility of per-device resolution normalizations where self-reported mouse DPI can change dynamically.
@slouken Would this fall under the purview of ABI lock-in or just API?
@slouken Would this fall under the purview of ABI lock-in or just API?
This is new functionality, so it can be added anytime.
I found that this commit has been restore in https://github.com/libsdl-org/SDL/commit/f37eef948ce7844e8c6310c8785a16919e28e21f Can there be an issue to track the progress of this?