libsdl-org / SDL

Simple Directmedia Layer
https://libsdl.org
zlib License
9.95k stars 1.84k forks source link

Cursor not constrained to window when using `SDL_SetRelativeMouseMode` and another game is running #7890

Open Susko3 opened 1 year ago

Susko3 commented 1 year ago

https://github.com/libsdl-org/SDL/blob/fb0c3197e0e4696551822815bdce0e187a0b06fc/include/SDL3/SDL_mouse.h#L200-L217

the mouse position is constrained to the window

When using relative mode, I would expect the cursor to be constrained to the window. But it appears this is not the case on windows.

Repro steps

  1. Run testwm.exe, I'm using testwm.exe --resizable --geometry 1150x900 --title testwm.exe to have a bigger window for convenience
  2. Press Ctrl+R to enable relative mode
  3. Move the mouse around, and notice that it can exit out of the window
    • note that MOUSE_FOCUS window flag will remain set, even if the mouse is outside the window (unlike when not using relative mode)

https://github.com/libsdl-org/SDL/assets/16479013/138af15f-e31b-44cc-89c1-a6a03b689675

I'm using PowerToys Mouse pointer Crosshairs to show where the actual OS mouse position is. Maybe this can aid debugging of this issue.

Environment

Edit: the bug seems to only happen if World of Tanks is open in the background. Could this invalidate the issue?

slouken commented 1 year ago

Interesting. It doesn't happen here on the same code and version of OS. It's very possible that World of Tanks is resetting the Windows cursor clip rectangle while in the background. That would be a bug in the game, and would definitely interfere with this test.

expikr commented 1 year ago

Try running this autoit script in the background instead of World of Tank and see if it recreates the behavior:

opt('guioneventmode',1)
guicreate('')
guisetstate()
guisetonevent(-3,quit)
while sleep(1000)
    dllcall('user32.dll','bool','ClipCursor','struct*',Null)
wend
func quit()
     exit
endfunc

A workaround would be to run GetClipCursor on every WM_MOUSMOVE and send ClipCursor if it's not the right clipping range. In relative mode you should never receive WM_MOUSEMOVE because the cursor is constrained to one pixel.

Susko3 commented 1 year ago

It's very possible that World of Tanks is resetting the Windows cursor clip rectangle while in the background. That would be a bug in the game, and would definitely interfere with this test.

This is exactly it.

I've run the C# equivalent of @expikr's while(true) ClipCursor(NULL); code and it behaves the same as having World of Tanks in the background. I'll be sure to report the bug to the devs.

SDL will update the ClipCursor every 3 seconds, so it can recover if an external app (accidentally) sets it to the wrong value once.

Susko3 commented 1 year ago

I'm eager to close this issue as "not SDL's problem", but I think adding logging would be helpful for pointing the blame onto other running programs.

Something like "ClipCursor rect changed unexpectedly (is another program changing it?). Resetting to expected location,"