Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Cursor problems on Wayland #71

Closed
vanfanel opened this issue Jul 3, 2023 · 30 comments
Closed

Cursor problems on Wayland #71

vanfanel opened this issue Jul 3, 2023 · 30 comments
Labels

Comments

@vanfanel
Copy link
Contributor

vanfanel commented Jul 3, 2023

Describe the bug
OpenFodder cursor moves erratically on Wayland unless I set alternate-mouse=true in openfodder.ini
The problem is that, this way, mouse is "sticky" when moving it beyond the lower and right screen limits.

My understanding is that SDL2 mouse grabbing doesn't seem to work "as expected" on Wayland, as it does on X11.

To Reproduce
Run the engine on Wayland,
Move the cursor.

Expected behavior
Cursor should behave normally on Wayland too.

Desktop (please complete the following information):

  • OS: Debian GNU/Linux
  • Version 12
@vanfanel
Copy link
Contributor Author

vanfanel commented Jul 3, 2023

There's more information here:

https://wiki.archlinux.org/title/wayland

Look for "Input grabbing in games, remote desktop and VM windows", where it's explained.

@segrax
Copy link
Member

segrax commented Jul 3, 2023

OF doesn't actually grab the mouse,

however; the x/y position is set every cycle of the engine. I have seen issues with this before.. possibly in remote desktop and/or VNC

its currently implemented how the original engine does it, alternative-mouse was the approach we used at first, but continually ran into problems and it was eventually scrapped (although it ended up coming back for the Emscripten build or remote desktop.. i cant recall exactly)

behavior is:

every cycle of the engine,

  • system mouse x/y is read
  • system mouse x/y is set to center of the window
  • difference between center and the last read x/y is added to internal cursor position

Behavior only varies from the original when the internal mouse x/y passes the screen edge or when the window loses focus, when that happens we stop setting the mouse x/y

@vanfanel
Copy link
Contributor Author

vanfanel commented Jul 3, 2023

@segrax I have found the problem with OpenFodder in Wayland.

The OpenFodder engine uses SDL_WarpMouseGlobal() and SDL_WarpMouseInWindow(), here:

SDL_WarpMouseInWindow( mWindow, pPosition.getX(), pPosition.getY() );

SDL_WarpMouseGlobal(pPosition.getX(), pPosition.getY());

That does NOT work on Wayland, since SDL_WarpMouse* functions are unsupported on Wayland:
https://github.com/libsdl-org/SDL/blob/b8d6023a918c3930861c42ea5ea207da28ce9182/src/video/wayland/SDL_waylandmouse.c#L622
As you will see there, all possible outcomes of Wayland_WarpMouse() end in SDL_Unsupported() unless the cursor is invisible.

So, the solution would be avoiding SDL_WarpMouse* functions.

This is how Scummvm fixed the cursor problems in Wayland:

scummvm/scummvm@80e1695

And this is a previous discussion with SDL2 devs.

In a nutshell, please use SDL_SetWindowMouseRect instead of SDL_WarpMouse*

@segrax
Copy link
Member

segrax commented Jul 3, 2023

SDL_SetWindowMouseRect only restricts the cursor to a rectangle area inside the window, doing this wont have the desired outcome

The purpose of setting the cursor location to window center is so cursor acceleration speed can be calculated per engine cycle
but its also used to update the position of the internal cursor x/y,
which is why in alternate-mouse, when the camera pans, the cursor moves (as the cursor is now using the raw x/y relative to the window).. whereas in the original, the cursor remains over the tile it was hovering before the camera pan begun

@vanfanel
Copy link
Contributor Author

vanfanel commented Jul 3, 2023

Ouch, then I don't know...
Well, I will wait patiently. Wayland is almost everywhere so the engine will have to adapt someday :D

@segrax
Copy link
Member

segrax commented Jul 4, 2023

Which version of SDL2 are you compiling against?

SDL_HINT_VIDEO_WAYLAND_EMULATE_MOUSE_WARP was added in 2.26.0

@vanfanel
Copy link
Contributor Author

vanfanel commented Jul 4, 2023

I am building against latest SDL2 stable: 2.28.0

As for SDL_HINT_VIDEO_WAYLAND_EMULATE_MOUSE_WARP, it seems that only works under certain circumstances:
https://discourse.libsdl.org/t/sdl-wayland-emulate-mouse-warp-using-relative-mouse-mode/39112

Well, at least here, it doesn't work as intended.

If there's something I can test in the code to try and solve this, tell me so I can rebuild and test.
I think this engine should work correctly on Wayland.

@segrax
Copy link
Member

segrax commented Jul 4, 2023

it would seem "fixing" this isn't going to be a simple/quick task

its going to require some sort of overhaul/replacement of a number of pieces of functionality relating to the mouse, window and camera panning.

@drnovice do you remember the mouse issues back in 2018 and what else we tried?
There was a few approaches tested which all ran into walls.

To make matters more complex, the ingame sidebar is referenced in terms of negative X values.
The current solution was the last option we wanted to implement, but its also the one which matches the original Amiga/Dos behavior. it also took a week of tinkering to get it perfect (cursor leaving/entering the window at the exact right time)

Some quick thoughts/issues

  • cursor leaving the window needs to be in sync with system cursor to look fluid
  • In game cursor position must remain static when camera pans, unless mouse is still moving
  • cursor has to be able to go to -32 and -4 before it leaves the window
  • window size can change at any time which affects scale, and tracking if an internal x/y position is used instead of actual relative position
  • actual relative position is problematic because it cant goto -32 and -4

@segrax
Copy link
Member

segrax commented Jul 5, 2023

Knowns

  • Mouse position follows the terrain when camera moves (func: Camera_Update_Mouse_Position_For_Pan())
  • Playfield begins at X=0, Sidebar begins at X=-32
  • On wayland we cant change the mouse x/y

Problems

  • using the system provided x/y for cursor, we cant change the cursor position when the camera pans
  • using internal x/y to track position, the cursor goes out of sync with the system provided x/y, and cursor will leave window when user isn't expecting it

Possible Options

  • Force full screen on Wayland (then the real cursor position is irrelevant and we can just track changes between cycles) -- or is it? what happens when it reaches the screen edge
  • Force alternate mouse on Wayland in Window mode

@vanfanel
Copy link
Contributor Author

vanfanel commented Jul 6, 2023

@segrax Fullscreen on Wayland: cursor works right only when alternate-mouse=true, but even then, it gets "stuck" on the left and down edges of the screen.
What's happening (I guess) is that the cursor coordinates go beyond the game area.
Using SDL_SetWindowMouseRect would correct that, I guess.

@drnovice
Copy link
Contributor

drnovice commented Jul 6, 2023

@vanfanel I believe the problem is to refer to SDL2 current (lack of) support for Wayland.

SDL_SetWindowMouseRect is just a current workaround, but we can't twist the engine every time a new library or system tries to interface (without full compatibility) with what has been developed previously.

I can understand that now you're on Wayland (because of the very short time I don't know it at all, but my world is under microsoft azure and c# technologies, c++ systems always remains a "hobby" for me) but you have to also understand that the latest big development on these SDL2 primitive was done in 2018, it's been 5 years ago.

The good news is that this is an opensource project, if you can find support (with the current version of SDL2) on OF for Wayland that is backwards compatible to X11 of course, feel free to fork the project and send in the PR: we will it evaluate carefully.

@vanfanel
Copy link
Contributor Author

vanfanel commented Jul 6, 2023

@drnovice Wayland support in SDL2 is mature and bugs are constantly being worked out.
Where do you get the 2018 figure from??
https://github.com/libsdl-org/SDL/tree/main/src/video/wayland

Also, do you imply that incorporating SDL_SetWindowMouseRect as a workaround won't be done?
I am not a native english speaker, so many subtleties are beyond me.
But I think it's an easy fix for someone familiar with OF code.

The function is pretty simple:
https://wiki.libsdl.org/SDL2/SDL_SetWindowMouseRect
It should be called when alternate-mouse=true is used, as simple as that, and the game would be fixed on Wayland.

Note that this solution is also X11 backwards compatible.

@drnovice
Copy link
Contributor

drnovice commented Jul 6, 2023

SDL_SetWindowMouseRect could fix the SDL_WarpMouseInWindow function, where you work with the window.
But again, it would be temporary workaround: I would wait the full implementation of SDL about Wayland_WarpMouse() to use the right way the Wayland system.

And what about SDL_WarpMouseGlobal? It moves the cursor in the full screen background over the SDL window.

Wayland seems not work outside the window "relative" mode:
https://github.com/libsdl-org/SDL/blob/17e95345e39b2b3ce436ce3029175055c3d48869/src/video/wayland/SDL_waylandmouse.c#L670

Now I can't remember when SDL_WarpMouseGlobal is used (probably fullscreen mode?) but it's not so simple as it could seem.
I have no time to test on the code, @vanfanel could you try to check how to fix the global mouse position?

@drnovice
Copy link
Contributor

drnovice commented Jul 6, 2023

[...]
The function is pretty simple: https://wiki.libsdl.org/SDL2/SDL_SetWindowMouseRect It should be called when alternate-mouse=true is used, as simple as that, and the game would be fixed on Wayland.

Note that this solution is also X11 backwards compatible.

I don't recommend using "alternate-mouse=true" , the user experience in game it's different and you can't aim well by the cursor while camera pans, IIRC

@vanfanel
Copy link
Contributor Author

vanfanel commented Jul 6, 2023

Ok, then I will forget about the alternate-mouse workaround...

I am trying to understand. Am I right that SDL_WarpMouse* isn't implemented on Wayland because, as the comment you point out says, Wayland doesn't support getting the true global cursor position?

If that's the case, SDL_WarpMouse* on Wayland won't be implemented, right?

@drnovice
Copy link
Contributor

drnovice commented Jul 6, 2023

I think if it was implemented, it will be implement with a "fake mode" as done for Wayland_GetGlobalMouseState()
It's all new also for us, so we need patience to understand how to do the support, if possibile.

@vanfanel
Copy link
Contributor Author

vanfanel commented Jul 6, 2023

It doesn't seem it will be implemented...

libsdl-org/SDL#7937

@Kontrabant
Copy link

Kontrabant commented Jul 6, 2023

I think if it was implemented, it will be implement with a "fake mode" as done for Wayland_GetGlobalMouseState() It's all new also for us, so we need patience to understand how to do the support, if possibile.

SDL can fake the global coordinates because, on Wayland, windows are always considered to be at the origin of the last display they entered, so the window-relative coordinates can just be offset by the window origin, giving the correct window-relative coordinates when the transformation is reversed, which is what applications like the Unreal editor and most others that want global mouse coordinates do. It doesn't work at all when the cursor is outside an application window.

Wayland doesn't support warping the mouse cursor position at all, either locally or globally, for security reasons, so the only case where it can be faked is in relative mode. If an application really needs full control over the cursor position within window bounds, it would need to use relative mode and draw its own software cursor that it has 100% control over.

It doesn't seem it will be implemented...

Honestly, would if we could, but unless Wayland gains support for it, we can't.

@segrax
Copy link
Member

segrax commented Jul 6, 2023

Thanks for the explanation @Kontrabant
Seems I've been able to replicate the original behavior using SDL_SetRelativeMouseMode and SDL_HINT_MOUSE_RELATIVE_MODE_WARP

the cursor transition from inside to outside the window is a little rough, but only on platforms where SDL_WarpMouseGlobal doesnt work

@vanfanel I've just pushed the changes if you'd like to give it a test

@vanfanel
Copy link
Contributor Author

vanfanel commented Jul 7, 2023

@segrax Cursor movement with alternate-mouse=false is now good!

However, when I click, the cursor is teleported to the top of the screen (and the click has no effect).

NOTE: I tested it as soon as I got home... sorry I couldn't test earlier.

@segrax
Copy link
Member

segrax commented Jul 8, 2023

@vanfanel no problem, no rush!

Ok i see, just learnt that SDL_GetGlobalMouseState is also not supported on Wayland, which we only use to determine if the cursor is over the window.. now replaced with sdl_getmousefocus,

have just pushed another fix, see how that goes

@vanfanel
Copy link
Contributor Author

vanfanel commented Jul 8, 2023

@segrax Same problem: the cursor is teleported to the upper part of the screen when clicking.

@segrax
Copy link
Member

segrax commented Jul 9, 2023

hm i think i know whats happening, but really have no idea why.

given a lack of a setup with wayland, and no time right now to set one up...
line 2876 in Fodder.cpp,
mWindow->SetRelativeMouseMode(false);

try comment it out/remove it.. if it works the cursor wont be able to leave the window

@vanfanel
Copy link
Contributor Author

vanfanel commented Jul 9, 2023

Still the exact same problem: cursor is teleported to the upper border of the window when clicking on any part of the screen (or the upper border of the window, if the game is in windowed mode)

@segrax
Copy link
Member

segrax commented Jul 9, 2023

when you say cursor in this instance, is that the in-game cursor, or the system cursor?

@vanfanel
Copy link
Contributor Author

vanfanel commented Jul 9, 2023

I mean the in-game cursor (there's only that cursor once I launch the game).

@segrax
Copy link
Member

segrax commented Jul 9, 2023

ok last guess and then it will have to wait until i can setup something to debug on,

in Window.cpp, line41: mHasFocus = false;
try
mHasFocus = true;

@vanfanel
Copy link
Contributor Author

@segrax That change doesn't have any impact on the issue, either.

@segrax
Copy link
Member

segrax commented Jul 11, 2023

@vanfanel ok i found the issue, at-least one of them

During a button press (SDL_MOUSEBUTTONUP or SDL_MOUSEBUTTONDOWN), on Windows xrel and yrel are set to zero. but on Linux, im getting high/strange values, the yrel is typically in the negative, thus causing the cursor to head to the top border.
not sure if theres a different data structure in play or not. but seems to be the cause

Event.mPositionRelative = cPosition(SysEvent.motion.xrel, SysEvent.motion.yrel);

ive just pushed a fix for it

@vanfanel
Copy link
Contributor Author

@segrax Thanks, this worked!
OpenFodder now works perfectly on Wayland! Yay! :D

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

4 participants