From 98ce013242050add7e891952ba0bd10f26b91a07 Mon Sep 17 00:00:00 2001 From: ocornut Date: Wed, 12 Jan 2022 16:44:24 +0100 Subject: [PATCH] Backends: SDL: Update mouse inputs using SDL_MOUSEMOTION/SDL_WINDOWEVENT_LEAVE + fallback to provide it when focused but not hovered/captured + update MousePos before MouseButtons. --- backends/imgui_impl_sdl.cpp | 85 ++++++++++++++++--------------------- docs/CHANGELOG.txt | 1 + 2 files changed, 37 insertions(+), 49 deletions(-) diff --git a/backends/imgui_impl_sdl.cpp b/backends/imgui_impl_sdl.cpp index 8e61e861..f0109547 100644 --- a/backends/imgui_impl_sdl.cpp +++ b/backends/imgui_impl_sdl.cpp @@ -18,6 +18,7 @@ // CHANGELOG // (minor and older changes stripped away, please see git history for details) +// 2022-01-12: Update mouse inputs using SDL_MOUSEMOTION/SDL_WINDOWEVENT_LEAVE + fallback to provide it when focused but not hovered/captured. More standard and will allow us to pass it to future input queue API. // 2022-01-12: Maintain our own copy of MouseButtonsDown mask instead of using ImGui::IsAnyMouseDown() which will be obsoleted. // 2022-01-10: Inputs: calling new io.AddKeyEvent(), io.AddKeyModsEvent() + io.SetKeyEventNativeData() API (1.87+). Support for full ImGuiKey range. // 2021-08-17: Calling io.AddFocusEvent() on SDL_WINDOWEVENT_FOCUS_GAINED/SDL_WINDOWEVENT_FOCUS_LOST. @@ -233,6 +234,11 @@ bool ImGui_ImplSDL2_ProcessEvent(const SDL_Event* event) switch (event->type) { + case SDL_MOUSEMOTION: + { + io.MousePos = ImVec2((float)event->motion.x, (float)event->motion.y); + return true; + } case SDL_MOUSEWHEEL: { if (event->wheel.x > 0) io.MouseWheelH += 1; @@ -270,6 +276,8 @@ bool ImGui_ImplSDL2_ProcessEvent(const SDL_Event* event) } case SDL_WINDOWEVENT: { + if (event->window.event == SDL_WINDOWEVENT_LEAVE) + io.MousePos = ImVec2(-FLT_MAX, -FLT_MAX); if (event->window.event == SDL_WINDOWEVENT_FOCUS_GAINED) io.AddFocusEvent(true); else if (event->window.event == SDL_WINDOWEVENT_FOCUS_LOST) @@ -391,61 +399,42 @@ void ImGui_ImplSDL2_Shutdown() IM_DELETE(bd); } -static void ImGui_ImplSDL2_UpdateMousePosAndButtons() +static void ImGui_ImplSDL2_UpdateMouseData() { ImGui_ImplSDL2_Data* bd = ImGui_ImplSDL2_GetBackendData(); ImGuiIO& io = ImGui::GetIO(); - ImVec2 mouse_pos_prev = io.MousePos; - io.MousePos = ImVec2(-FLT_MAX, -FLT_MAX); + // We forward mouse input when hovered or captured (via SDL_MOUSEMOTION) or when focused (below) +#if SDL_HAS_CAPTURE_AND_GLOBAL_MOUSE + // SDL_CaptureMouse() let the OS know e.g. that our imgui drag outside the SDL window boundaries shouldn't e.g. trigger other operations outside + SDL_CaptureMouse(bd->MouseButtonsDown != 0 ? SDL_TRUE : SDL_FALSE); + SDL_Window* focused_window = SDL_GetKeyboardFocus(); + const bool is_app_focused = (bd->Window == focused_window); +#else + const bool is_app_focused = (SDL_GetWindowFlags(bd->Window) & SDL_WINDOW_INPUT_FOCUS) != 0; // SDL 2.0.3 and non-windowed systems: single-viewport only +#endif + if (is_app_focused) + { + // (Optional) Set OS mouse position from Dear ImGui if requested (rarely used, only when ImGuiConfigFlags_NavEnableSetMousePos is enabled by user) + if (io.WantSetMousePos) + SDL_WarpMouseInWindow(bd->Window, (int)io.MousePos.x, (int)io.MousePos.y); - // Update mouse buttons - int mouse_x_local, mouse_y_local; - Uint32 mouse_buttons = SDL_GetMouseState(&mouse_x_local, &mouse_y_local); + // (Optional) Fallback to provide mouse position when focused (SDL_MOUSEMOTION already provides this when hovered or captured) + if (bd->MouseCanUseGlobalState && bd->MouseButtonsDown == 0) + { + int window_x, window_y, mouse_x_global, mouse_y_global; + SDL_GetGlobalMouseState(&mouse_x_global, &mouse_y_global); + SDL_GetWindowPosition(focused_window, &window_x, &window_y); + io.MousePos = ImVec2((float)(mouse_x_global - window_x), (float)(mouse_y_global - window_y)); + } + } + + // Update buttons + Uint32 mouse_buttons = SDL_GetMouseState(NULL, NULL); io.MouseDown[0] = bd->MousePressed[0] || (mouse_buttons & SDL_BUTTON(SDL_BUTTON_LEFT)) != 0; // If a mouse press event came, always pass it as "mouse held this frame", so we don't miss click-release events that are shorter than 1 frame. io.MouseDown[1] = bd->MousePressed[1] || (mouse_buttons & SDL_BUTTON(SDL_BUTTON_RIGHT)) != 0; io.MouseDown[2] = bd->MousePressed[2] || (mouse_buttons & SDL_BUTTON(SDL_BUTTON_MIDDLE)) != 0; bd->MousePressed[0] = bd->MousePressed[1] = bd->MousePressed[2] = false; - - // Obtain focused and hovered window. We forward mouse input when focused or when hovered (and no other window is capturing) -#if SDL_HAS_CAPTURE_AND_GLOBAL_MOUSE - SDL_Window* focused_window = SDL_GetKeyboardFocus(); - SDL_Window* hovered_window = SDL_HAS_MOUSE_FOCUS_CLICKTHROUGH ? SDL_GetMouseFocus() : NULL; // This is better but is only reliably useful with SDL 2.0.5+ and SDL_HINT_MOUSE_FOCUS_CLICKTHROUGH. - SDL_Window* mouse_window = NULL; - if (hovered_window && bd->Window == hovered_window) - mouse_window = hovered_window; - else if (focused_window && bd->Window == focused_window) - mouse_window = focused_window; - - // SDL_CaptureMouse() let the OS know e.g. that our imgui drag outside the SDL window boundaries shouldn't e.g. trigger other operations outside - SDL_CaptureMouse(bd->MouseButtonsDown != 0 ? SDL_TRUE : SDL_FALSE); -#else - // SDL 2.0.3 and non-windowed systems: single-viewport only - SDL_Window* mouse_window = (SDL_GetWindowFlags(bd->Window) & SDL_WINDOW_INPUT_FOCUS) ? bd->Window : NULL; -#endif - - if (mouse_window == NULL) - return; - - // Set OS mouse position from Dear ImGui if requested (rarely used, only when ImGuiConfigFlags_NavEnableSetMousePos is enabled by user) - if (io.WantSetMousePos) - SDL_WarpMouseInWindow(bd->Window, (int)mouse_pos_prev.x, (int)mouse_pos_prev.y); - - // Set Dear ImGui mouse position from OS position + get buttons. (this is the common behavior) - if (bd->MouseCanUseGlobalState) - { - // 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) - // Unlike local position obtained earlier this will be valid when straying out of bounds. - int mouse_x_global, mouse_y_global; - SDL_GetGlobalMouseState(&mouse_x_global, &mouse_y_global); - int window_x, window_y; - SDL_GetWindowPosition(mouse_window, &window_x, &window_y); - io.MousePos = ImVec2((float)(mouse_x_global - window_x), (float)(mouse_y_global - window_y)); - } - else - { - io.MousePos = ImVec2((float)mouse_x_local, (float)mouse_y_local); - } } static void ImGui_ImplSDL2_UpdateMouseCursor() @@ -545,10 +534,8 @@ void ImGui_ImplSDL2_NewFrame() io.DeltaTime = bd->Time > 0 ? (float)((double)(current_time - bd->Time) / frequency) : (float)(1.0f / 60.0f); bd->Time = current_time; - // Update key modifiers ImGui_ImplSDL2_UpdateKeyModifiers(); - - ImGui_ImplSDL2_UpdateMousePosAndButtons(); + ImGui_ImplSDL2_UpdateMouseData(); ImGui_ImplSDL2_UpdateMouseCursor(); // Update game controllers (if enabled and available) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index e9b8e700..f9eb46b2 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -99,6 +99,7 @@ Other Changes: - Backends: Win32: Update mouse position using WM_MOUSEMOVE/WM_MOUSELEAVE + fallback when focused but not hovered/captured. - Backends: Win32: Maintain a MouseButtonsDown mask instead of using ImGui::IsAnyMouseDown() which will be obsoleted. - Backends: SDL: Pass localized keys (matching keyboard layout). Fix e.g. CTRL+A, CTRL+Z, CTRL+Y shortcuts. +- Backends: SDL: Update mouse position using SDL_MOUSEMOTION/SDL_WINDOWEVENT_LEAVE + fallback when focused but not hovered/captured. - Backends: SDL: Maintain a MouseButtonsDown mask instead of using ImGui::IsAnyMouseDown() which will be obsoleted. - Backends: Allegro5, GLFW, GLUT, SDL, OSX, Win32, Android: Updated to use io.AddKeyEvent() with full key range. (#2625) [@thedmd] - Backends: OpenGL3: Fixed a buffer overflow in imgui_impl_opengl3_loader.h init (added in 1.86). (#4468, #4830) [@dymk]