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

sokol_app.h: initial mouse position #1170

Open
wants to merge 1 commit into
base: master
Choose a base branch
from

Conversation

Seb-degraff
Copy link
Contributor

Send an initial mouse move event just after init and before the first frame. That allows the app to know where the mouse is before mouse is actually moved.

My use case was a small topdown shooter game where you aim with the mouse. At app launch the aim target was the top-left corner of the window, and as soon as you moved the mouse the target would jump, also teleporting the camera, which was jarring.

I initially wanted to modify only applicationDidFinishLaunching and send the event from there, but the user code might do some setup in init and not be ready to receive a move event before (the samples do that with dear imgui). Therefore I put the sending of the event in _sapp_frame, just after init.

If that sounds like a good improvement, I can implement the other platforms. I'll look at windows now.

Send an initial mouse move event just after init and before the first frame. That allows the app to know where the mouse is before mouse is actually moved.
@Seb-degraff
Copy link
Contributor Author

To test, run the event-sapp sample on macos, and notice that the MOUSE_MOVE box shows the correct mouse pos value. Previously it would show 0.00 0.00

@floooh
Copy link
Owner

floooh commented Dec 20, 2024

Hmm, that sounds like a weird use case tbh... wouldn't it be better to disable the aiming mechanism (and visualization) until the first 'proper' mouse event is received?

From googling around it looks like on HTML5 it's not possible to poll the mouse position, so any mechanism to get an initial mouse position without the mouse being moved first wouldn't work there.

Does GLFW have a solution for that problem (because the GLFW code base is sort-of the reference for sokol_app.h, it doesn't help with HTML5 problems though).

@floooh
Copy link
Owner

floooh commented Dec 20, 2024

PS: please ignore the broken CI build, that's a problem with the sokol-d build.zig not being fixed for zig 0.14.x

@Seb-degraff
Copy link
Contributor Author

Hmm, that sounds like a weird use case tbh...

Yes, this example was quite specific. I had the "problem" before with other things though.
For example: consider an app with a menu at the top of the window. The app draws a highlight effect on the menu items when the mouse is over them. It checks the mouse position by storing an internal mouse pos and updating it as mouse_move events arrive. When the app launches and no mouse_move event have been received yet, the app might use a default mouse pos of zero. Then when menus are drawn, the item at the top left will be highlighted until the first move mouse event is received.

Screenshot 2024-12-20 at 18 27 43

I see three ways to fix it:

  • the app can start with an internal mouse pos value of -1000, -1000 or something. That would solve the problem for the menu example, but not for the shooter example, and it feels like a hack
  • on top of storing the mouse pos, the app can store whether or not the current position is valid / known. This slightly complicate usage code as the menu drawing code has to remember to check mouse_pos_is_valid() on top of just checking the mouse pos for overlap
  • the app can start with a valid mouse pos value, and for that it needs a mechanism to get it. I thought sending an initial event would work well, but I guess it can feel surprising depending on how you look at it. Maybe something like sapp_get_mouse_position could also be considered (glfw seems to have this). Though sticking to events feels simpler to me.

wouldn't it be better to disable the aiming mechanism (and visualization) until the first 'proper' mouse event is received?

Yes, could definitely implement mouse_pos_is_valid() and not aim or offset the camera as long as it returns false. But I think it would be great if user code didn't have to worry about it (not just for this specific example, but for all games/app using sokol and storing + polling the mouse position in that way).

For the web it's unfortunate that the mouse pos is not accessible from the start. But since many things require user interaction to start working anyway (e.g sound) I think many people require the user to focus the app/game before starting already, and I presume this would send a mouse_move event?

I tested on windows and it happened to work with no change beyond what I did for macos. It seems that windows sends a WM_MOUSEMOVE event at startup, which updates the internal sokol mouse pos even if the sokol_event is never sent (since it happens before init_cb is called).

@floooh
Copy link
Owner

floooh commented Dec 22, 2024

I need to think about this a bit more.

I was thinking that sokol_app.h should track an internal is_mouse_pos_valid flag which is set to true on the first received mouse move event, and that this flag should also go into sapp_event (since all events also have mouse-pos fields).

But it turns out that on some platforms the current mouse position is also sent in non-mouse-move events (from the operating system), and in that case the sapp_event mouse position is overwritten with those uptodate values instead of using the internally stored mouse position that's tracked via mouse move events (same for the mouse delta, on some platforms this is taken directly from OS events instead of computing it from the tracked mouse position).

So it actually is possible to receive a mouse position and delta piggybacked on another OS event before the first mouse-move event arries.

...and in the past there have also been fixes to make such mouse positions reported in non-mouse-move events work before the first mouse-move event is received.

There is still a phase after start where the internally tracked mouse position isn't valid, but it is not necessarily the first mouse move event which provides the first valid mouse position, it can be any other OS-specific event.

...it might still make sense to add a flag to sapp_event of whether the embedded mouse data is valid, but tracking this correctly across all platforms might require cleanup work in the sokol_app.h mouse input code.

As a conversative workaround I would suggest that you track your own mouse state, something like:

typedef struct {
    float x, y;
    bool valid;
} mouse_pos_t;

...and in the sokol-app event handler function, update this when receiving mouse-button or -move events:

...
    case SAPP_EVENTTYPE_MOUSE_DOWN:
    case SAPP_EVENTTYPE_MOUSE_UP:
    case SAPP_EVENTTYPE_MOUSE_MOVE:
        mouse_pos.valid = true;
        mouse_pos.x = ev->mouse_x;
        mouse_pos.y = ev->mouse_y;
        break;
...

@Seb-degraff
Copy link
Contributor Author

I already do what you suggest, but want to get the mouse pos at app launch, before the user moves the mouse (to remove the requirement of checking that the position is valid every time I need the position). Passing the mouse pos via another event at app launch would 100% fulfil that need.

Personally, I think sending an initial mouse_event is also fine and actually makes some sense — you don't know what the mouse was doing just before app launch, so you might as well pretend it "moved". The fact that Windows does this too (it seems to send a WM_MOUSEMOVE to the window when it opens) conforts me in that idea.

In any case I think this is a pretty small issue, more a case of polish than an actual problem, and I'm perfectly happy patching it on my side for my own use. So if you want to avoid wasting time and brain energy on this we can close the issue

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