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

Cannot filter SDL3 events to ImGui #8407

Open
TheMode opened this issue Feb 18, 2025 · 11 comments
Open

Cannot filter SDL3 events to ImGui #8407

TheMode opened this issue Feb 18, 2025 · 11 comments

Comments

@TheMode
Copy link

TheMode commented Feb 18, 2025

Version/Branch of Dear ImGui:

Branch: master

Back-ends:

imgui_impl_sdl3.cpp + imgui_impl_sdlrenderer3.cpp

Compiler, OS:

Windows 11 + MSCV 2022

Full config/build information:

No response

Details:

I wish to filter events provided to ImGui based on the used mouse/keyboard/gamepad device, which work fine for some inputs, but some others are retrieved globally bypassing event processing: this is the case for mouse motion and gamepads.

This is caused by those global functions: (also present in the GLFW backend)

ImGui_ImplSDL3_UpdateMouseData();
ImGui_ImplSDL3_UpdateMouseCursor();
// Update game controllers (if enabled and available)
ImGui_ImplSDL3_UpdateGamepads();

I would appreciate a way to (perhaps optionally) only rely on processed events whenever possible.

Screenshots/Video:

No response

Minimal, Complete and Verifiable Example code:

It is possible to hover the button without processing any event.

ImGui::Begin("Example Bug");
ImGui::Button("button")
ImGui::End();
@GamingMinds-DanielC
Copy link
Contributor

What do you mean by "used" devices? If the user points the mouse at something or presses a button on the keyboard or a gamepad, he pretty clearly intends to use that device. Unused devices usually don't have any buttons pressed.

You can get rid of gamepads in general by clearing ImGuiConfigFlags_NavEnableGamepad in ImGui::GetIO().ConfigFlags. If you need to use specific gamepads and ignore others, you are out of luck though with the supplied backends, same with global mouse state.

If you really need more control than the existing backends can give you, you can always use a custom backend. It doesn't have to be complicated to make one, you don't need to create one from scratch. You can fork an existing one and customize it to your specific needs. Makes updating to a newer version harder though.

@TheMode
Copy link
Author

TheMode commented Feb 18, 2025

I am making a sort of desktop environment, the user may have more than one mouse and keyboard and so I plan to allow the user to decide which input is the "UI input" and let the rest be used freely without unexpected interaction.

My problem is mostly that ImGui makes uses of both events AND global state, couldn't events suffice? I already have the filter otherwise.

@TheMode
Copy link
Author

TheMode commented Feb 18, 2025

Coming back, I have actually found the mouse update function to be faulty once in relative mode.

I have been using this code to capture the mouse while still being bound to window coordinate: (XY: necessary if I want to listen to multiple mice at once)

SDL_SetWindowRelativeMouseMode(app->window, true);
SDL_SetWindowMouseRect(app->window, nullptr);

The problem is that once in relative mode, SDL_GetGlobalMouseState stops being accurate to track internal mouse position and start returning a constant.

Which in turn cause io.MousePosPrev (due to ImGui global update) to constantly return window's center

// Single-viewport mode: mouse position in client window coordinates (io.MousePos is (0,0) when the mouse is on the upper-left corner of the app window)
float mouse_x_global, mouse_y_global;
int window_x, window_y;
SDL_GetGlobalMouseState(&mouse_x_global, &mouse_y_global);
SDL_GetWindowPosition(focused_window, &window_x, &window_y);
io.AddMousePosEvent(mouse_x_global - window_x, mouse_y_global - window_y);

Simple reproducer:

float mouse_x, mouse_y;
SDL_GetMouseState(&mouse_x, &mouse_y);
SDL_Log("Mouse: %f, %f", mouse_x, mouse_y);
if (SDL_abs(io.MouseDelta.x) > 10 || SDL_abs(io.MouseDelta.y) > 10) {
  SDL_Log("Prev: %f, %f", io.MousePosPrev.x, io.MousePosPrev.y);
  SDL_Log("Cur: %f, %f", io.MousePos.x, io.MousePos.y);
  SDL_Log("Delta: %f, %f", io.MouseDelta.x, io.MouseDelta.y);
}

On my 1280x720 window:

Mouse: 330.000000, 401.000000
Mouse: 331.000000, 402.000000
Prev: 640.000000, 360.000000
Cur: 331.000000, 402.000000
Delta: -309.000000, 42.000000

Events on the other hand are still correct.

@ocornut
Copy link
Owner

ocornut commented Feb 18, 2025

As far as I understand, your only issue has to do with the mouse. You can already fully filter keyboard and gamepad via other means (keyboard is 100% events, gamepad via ImGui_ImplSDL3_SetGamepadMode()).

I am not even sure I know how SDL3 handle multiple mices, how would be call SDL_CaptureMouse(), SDL_WarpMouseInWindow() in that situation?

The path we use SDL_GetGlobalMouseState() is desirable because it allow us to obtain mouse pos outside of main window boundaries. If you e.g. have a tooltip following mouse, or some kind of overlay, you want that mouse position. But we could decide it is an optional feature.

If there are way to call SDL_CaptureMouse(), SDL_WarpMouseInWindow() and SDL_GetGlobalMouseState() on specific mouse id it would likely solve your problem.

@TheMode
Copy link
Author

TheMode commented Feb 18, 2025

From what I understand, relative mode is the only way to support multiple mice: libsdl-org/SDL#11259 (comment)

I can then retrieve the mouse id on each event.

@ocornut
Copy link
Owner

ocornut commented Feb 18, 2025

I'd suggest you try to modify/patch the SDL3 backend for your need but I don't see an easy path ahead for us to maintain a backend supporting multiple mices.

@TheMode
Copy link
Author

TheMode commented Feb 18, 2025

Couldn't the backend only use SDL_GetGlobalMouseState when the value is determined outside the window?

Even ignoring my multiple mice problem, relative mouse does cause problem when there is only one. Perhaps I could try to figure out how to change the default mouse so imgui global functions ultimately only reach the mouse I have.

@ocornut
Copy link
Owner

ocornut commented Feb 18, 2025

Couldn't the backend only use SDL_GetGlobalMouseState when the value is determined outside the window?

It could but that wouldn't solve your problem since it'd eventually still be called? (along with the other functions)

@TheMode
Copy link
Author

TheMode commented Feb 18, 2025

Something like this in ImGui_ImplSDL3_UpdateMouseData would at least solve the issue with a single mouse in relative mode (quickly tested on my side, hopefully I am not breaking anything)

// Single-viewport mode: mouse position in client window coordinates (io.MousePos is (0,0) when the mouse is on the upper-left corner of the app window)
if (SDL_GetWindowRelativeMouseMode(bd->Window)) {
    // `SDL_GetGlobalMouseState` is invalid in relative mouse mode
    float mouse_x, mouse_y;
    SDL_GetMouseState(&mouse_x, &mouse_y);
    io.AddMousePosEvent(mouse_x, mouse_y);
} else {
    float mouse_x_global, mouse_y_global;
    int window_x, window_y;
    SDL_GetGlobalMouseState(&mouse_x_global, &mouse_y_global);
    SDL_GetWindowPosition(focused_window, &window_x, &window_y);
    io.AddMousePosEvent(mouse_x_global - window_x, mouse_y_global - window_y);
}

I would then have to find (on my project) a way to change the default mouse used by SDL global functions so I can support multiple mice.

EDIT: or even better, don't call the global update in relative mode, the mouse cannot be outside the window so it seems safe, and it then solely rely on events (which I can filter myself).

if (!SDL_GetWindowRelativeMouseMode(bd->Window)) ImGui_ImplSDL3_UpdateMouseData();

Again, warn me if I am missing the obvious.

@ocornut
Copy link
Owner

ocornut commented Feb 18, 2025

if (!SDL_GetWindowRelativeMouseMode(bd->Window)) ImGui_ImplSDL3_UpdateMouseData();

The function does other things, such as capture and teleporting mouse.
But yeah we could gate the SDL_GetGlobalMouseState() part.

@TheMode
Copy link
Author

TheMode commented Feb 18, 2025

All in favor of a solution not calling SDL_GetGlobalMouseState nor SDL_GetMouseState (which ultimately doesn't seem necessary in my snippet above)

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

No branches or pull requests

3 participants