SDL_WarpMouseInWindow() when does it warp?

SDL_WarpMouseInWindow() is one of the most confusing functions in SDL2 to me.
The docs say:
“Use this function to move the mouse to the given position within the window.”

and

“This function generates a mouse motion event.”

From my tests, the mouse warps immediately, but the corresponding mouse motion event is queued at the end of the queue.

How is this consistent with the rest of mouse handling? What if there are pending mouse motion events when this function is called? The event queue will then not accurately represent the true mouse motion.

Can someone have mercy with me and explain how I can avoid the problem of having mouse motion events out of order when I use this function? Is it possible to warp the mouse without generating a mouse motion event at the wrong place in the queue?

Should I just hack the internal SDL mouse state structure and write x, y values directly to circumvent this?

I’ve no direct experience with this function in particular, but… I’m not seeing any point of confusion?

The end of the queue seems like the right place to put the event; the mouse warp happened after any event which might already be in the queue, so its event gets delivered after, also. Like… if the user has already (for example) pressed a key before you warped the mouse, the keydown event will come to you before the warp’s mousemotion event, exactly as actually happened. Similarly, if the user has moved the mouse before you warp it, you’ll get the mouse motion for the move they made, and then the mouse motion from the warp. That… sounds right to me? It’s what actually happened, right?

What is the actual problem you’re seeing, or what is the behaviour that you expected?

(Edit: A lot of the time, folks use WarpMouse functions when SDL_SetWindowGrab() or SDL_SetRelativeMouseMode() would have done the job for them in a much easier way. I don’t know what your specific use-case is, but if you’re just trying to keep the mouse from leaving the window, then setting window grab mode would be a lot easier than manually warping the mouse. Similarly, if you’re trying to implement turning controls for a standard FPS control scheme, then relative mouse mode is exactly what you want; either way, you can avoid manually warping the mouse at all.

I guess the confusion is that you could have a mouse even that sets to cursor to (x1, y1) still in the queue, and after calling SDL_WarpMouseInWindow(x2, y2) you first get the (x1, y1) event and later the one for (x2, y2), even though when you receive (x1, y1) it already is at the other position because warping happens immediately (=> immediately observable via SDL_GetMouseState())?
(I haven’t verified that it actually happens like this, this is just how I understood @rsn8887’s post)

I agree that if you can avoid warping the mouse yourself by using SDL_SetWindowGrab() or SDL_SetRelativeMouseMode() you should do that.

SDL_GetMouseState() tells you the state of the mouse right now, immediately.

The event system tells you about all changes in state, and holds on to them until you get around to pulling the events from it. There’s no real conflict between them; it’s just that one is telling you the ‘current’ state, and the other is giving you a record of historical events since the last time you checked for events.

For example, if between two rendered frames the user clicks the mouse button and releases it again, SDL_GetMouseState() won’t tell you about it; you’d never realise the mouse click happened at all, because the mouse button was up each time you called SDL_GetMouseState(). The event system, though, will tell you about the mouse down and mouse up which occurred in between the two frames.

Usually you’ll be reading data from just one or the other of the two systems, not both; either grab the mouse state immediately (in which case you can miss ‘events’ which occur between tests), or use the event system (which is more complicated, but means you won’t miss events which occurred entirely in between frames). As you’ve noted; the two systems aren’t — can’t be — kept absolutely in “sync” with each other. (Although it all works out to the same final state once you’ve worked through all the events in the queue). Either just grab the immediate current state of everything, or else use the event system to hear about every input event which occurred since the last time you checked. Whichever is more appropriate for what you’re doing.

2 Likes

Good points, @vectorstorm!

Thank you all for the great answers. It makes sense to me now. The key was the explanation that the event system and the state system are two different systems and that ideally only one or the other should be used, not both.

I realize now SDL2 is written to have a state and an event system that both work differently.

There is an asymmetry: the event system is read/write (via SDL_PushEvent()) but the state system is read only (e.g. no SDL_SetMouseState()).

My use case is input remapping in games. For this purpose, the event system is very flexible. It allows me to generate and push my own Keydown events when the user presses a joystick button to do joystick-to-keyboard remapping. Or I can create and push Mousemotion events whenever the user moves a finger on the touch screen to do touch-to-mouse remapping.

However, the state system is not that flexible. Contrary to the event system, it is read-only, with some exceptions like SDL_WarpMouse. It is impossible for me to push state changes like I can push events. If a game uses the state system at some point, then input remapping is harder.

I wish there were functions to update state such as SDL_SetMouseState or SDL_SetKeyboardState. Then I could for example change the mouse state whenever a touch event occurs to do touch-to-mouse etc.