From 5c7c293a66d7a659d4e6a1abdf18291ce004c07e Mon Sep 17 00:00:00 2001 From: ocornut Date: Thu, 5 Nov 2026 19:06:37 +0100 Subject: [PATCH] Backends: SDL2, SDL3: macOS: fixed stuck down keys/buttons when they were held in a viewport that gets destroyed. (#9121, #7853) Regression in 1.91.1 caused by #7853 --- backends/imgui_impl_sdl2.cpp | 14 +++++++++++--- backends/imgui_impl_sdl3.cpp | 12 ++++++++++-- docs/CHANGELOG.txt | 2 ++ 3 files changed, 23 insertions(+), 5 deletions(-) diff --git a/backends/imgui_impl_sdl2.cpp b/backends/imgui_impl_sdl2.cpp index bcc3b8595..7c7ca23f7 100644 --- a/backends/imgui_impl_sdl2.cpp +++ b/backends/imgui_impl_sdl2.cpp @@ -26,6 +26,7 @@ // CHANGELOG // (minor and older changes stripped away, please see git history for details) // 2026-XX-XX: Platform: Added support for multiple windows via the ImGuiPlatformIO interface. +// 2026-11-05: [Docking] macOS: fixed stuck down keys/buttons when they were held while the focused viewport gets destroyed. (#9121, #7853) // 2026-08-06: Fixed querying framebuffer scale when using Metal. (#5592) // 2026-07-15: Inputs: restore SDL_StartTextInput()/SDL_StopTextInput() in IME handler for on-screen keyboard support on Android/mobile. (#7636, #9474) // 2026-04-16: Made ImGui_ImplSDL2_GetContentScaleForWindow(), ImGui_ImplSDL2_GetContentScaleForDisplay() helpers return a minimum of 1.0f, as some Linux setup seems to report <1.0f value and this breaks scaling border size. (#9369) @@ -179,6 +180,7 @@ struct ImGui_ImplSDL2_Data bool IsWayland; bool IsVulkan; bool WantUpdateMonitors; + ImVector LastFrameDestroyedWindowIDs; // Mouse handling Uint32 MouseWindowID; @@ -468,7 +470,8 @@ bool ImGui_ImplSDL2_ProcessEvent(const SDL_Event* event) case SDL_MOUSEBUTTONDOWN: case SDL_MOUSEBUTTONUP: { - if (ImGui_ImplSDL2_GetViewportForWindowID(event->button.windowID) == nullptr) + ImGuiViewport* viewport = ImGui_ImplSDL2_GetViewportForWindowID(event->button.windowID); + if (viewport == nullptr && bd->LastFrameDestroyedWindowIDs.contains(event->key.windowID) == false) return false; int mouse_button = -1; if (event->button.button == SDL_BUTTON_LEFT) { mouse_button = 0; } @@ -494,7 +497,7 @@ bool ImGui_ImplSDL2_ProcessEvent(const SDL_Event* event) case SDL_KEYUP: { ImGuiViewport* viewport = ImGui_ImplSDL2_GetViewportForWindowID(event->key.windowID); - if (viewport == nullptr) + if (viewport == nullptr && bd->LastFrameDestroyedWindowIDs.contains(event->key.windowID) == false) return false; //IMGUI_DEBUG_LOG("SDL_KEY%s : key=0x%08X ('%s'), scancode=%d ('%s'), mod=%X, windowID=%d, viewport=%08X\n", // (event->type == SDL_KEYDOWN) ? "DOWN" : "UP ", event->key.keysym.sym, SDL_GetKeyName(event->key.keysym.sym), event->key.keysym.scancode, SDL_GetScancodeName(event->key.keysym.scancode), event->key.keysym.mod, event->key.windowID, viewport ? viewport->ID : 0); @@ -1100,6 +1103,8 @@ void ImGui_ImplSDL2_NewFrame() // Update game controllers (if enabled and available) ImGui_ImplSDL2_UpdateGamepads(); + + bd->LastFrameDestroyedWindowIDs.resize(0); } //-------------------------------------------------------------------------------------------------------- @@ -1153,6 +1158,7 @@ static void ImGui_ImplSDL2_CreateWindow(ImGuiViewport* viewport) sdl_flags |= (viewport->Flags & ImGuiViewportFlags_TopMost) ? SDL_WINDOW_ALWAYS_ON_TOP : 0; #endif vd->Window = SDL_CreateWindow("No Title Yet", (int)viewport->Pos.x, (int)viewport->Pos.y, (int)viewport->Size.x, (int)viewport->Size.y, sdl_flags); + vd->WindowID = SDL_GetWindowID(vd->Window); vd->WindowOwned = true; if (use_opengl) { @@ -1162,7 +1168,7 @@ static void ImGui_ImplSDL2_CreateWindow(ImGuiViewport* viewport) if (use_opengl && backup_context) SDL_GL_MakeCurrent(vd->Window, backup_context); - viewport->PlatformHandle = (void*)(intptr_t)SDL_GetWindowID(vd->Window); + viewport->PlatformHandle = (void*)(intptr_t)vd->WindowID; viewport->PlatformHandleRaw = nullptr; SDL_SysWMinfo info; SDL_VERSION(&info.version); @@ -1180,6 +1186,8 @@ static void ImGui_ImplSDL2_DestroyWindow(ImGuiViewport* viewport) { if (ImGui_ImplSDL2_ViewportData* vd = (ImGui_ImplSDL2_ViewportData*)viewport->PlatformUserData) { + ImGui_ImplSDL2_Data* bd = ImGui_ImplSDL2_GetBackendData(); + bd->LastFrameDestroyedWindowIDs.push_back(vd->WindowID); if (vd->GLContext && vd->WindowOwned) SDL_GL_DeleteContext(vd->GLContext); if (vd->Window && vd->WindowOwned) diff --git a/backends/imgui_impl_sdl3.cpp b/backends/imgui_impl_sdl3.cpp index 2b26db377..6018cf44e 100644 --- a/backends/imgui_impl_sdl3.cpp +++ b/backends/imgui_impl_sdl3.cpp @@ -24,6 +24,7 @@ // CHANGELOG // (minor and older changes stripped away, please see git history for details) // 2026-XX-XX: Platform: Added support for multiple windows via the ImGuiPlatformIO interface. +// 2026-11-05: [Docking] macOS: fixed stuck down keys/buttons when they were held while the focused viewport gets destroyed. (#9121, #7853) // 2026-03-25: [Docking] Use SDL_HAS_EVENT_DISPLAY_USABLE_BOUNDS_CHANGED (SDL 3.4.0+) when available to avoid refreshing monitor work area every frame on Windows. (#8415) // 2026-03-09: [Docking] Fixed an issue dated 2025/04/09 (1.92 WIP) where a refactor+merge caused ImGuiBackendFlags_HasMouseHoveredViewport to never be set, causing foreign windows to be ignored when deciding of hovered viewport. (#9284) // 2026-02-13: Inputs: systems other than X11 are back to starting mouse capture on mouse down (reverts 2025-02-26 change). Only X11 requires waiting for a drag by default (not ideal, but a better default for X11 users). Added ImGui_ImplSDL3_SetMouseCaptureMode() for X11 debugger users. (#3650, #6410, #9235) @@ -128,6 +129,7 @@ struct ImGui_ImplSDL3_Data bool IsWayland; bool IsVulkan; bool WantUpdateMonitors; + ImVector LastFrameDestroyedWindowIDs; // IME handling SDL_Window* ImeWindow; @@ -437,7 +439,8 @@ bool ImGui_ImplSDL3_ProcessEvent(const SDL_Event* event) case SDL_EVENT_MOUSE_BUTTON_DOWN: case SDL_EVENT_MOUSE_BUTTON_UP: { - if (ImGui_ImplSDL3_GetViewportForWindowID(event->button.windowID) == nullptr) + ImGuiViewport* viewport = ImGui_ImplSDL3_GetViewportForWindowID(event->button.windowID); + if (viewport == nullptr && bd->LastFrameDestroyedWindowIDs.contains(event->button.windowID) == false) return false; int mouse_button = -1; if (event->button.button == SDL_BUTTON_LEFT) { mouse_button = 0; } @@ -463,7 +466,7 @@ bool ImGui_ImplSDL3_ProcessEvent(const SDL_Event* event) case SDL_EVENT_KEY_UP: { ImGuiViewport* viewport = ImGui_ImplSDL3_GetViewportForWindowID(event->key.windowID); - if (viewport == nullptr) + if (viewport == nullptr && bd->LastFrameDestroyedWindowIDs.contains(event->key.windowID) == false) return false; //IMGUI_DEBUG_LOG("SDL_EVENT_KEY_%s : key=0x%08X ('%s'), scancode=%d ('%s'), mod=%X, windowID=%d, viewport=%08X\n", // (event->type == SDL_EVENT_KEY_DOWN) ? "DOWN" : "UP ", event->key.key, SDL_GetKeyName(event->key.key), event->key.scancode, SDL_GetScancodeName(event->key.scancode), event->key.mod, event->key.windowID, viewport ? viewport->ID : 0); @@ -1040,6 +1043,8 @@ void ImGui_ImplSDL3_NewFrame() // Update game controllers (if enabled and available) ImGui_ImplSDL3_UpdateGamepads(); + + bd->LastFrameDestroyedWindowIDs.resize(0); } //-------------------------------------------------------------------------------------------------------- @@ -1101,6 +1106,7 @@ static void ImGui_ImplSDL3_CreateWindow(ImGuiViewport* viewport) sdl_flags |= (viewport->Flags & ImGuiViewportFlags_NoTaskBarIcon) ? SDL_WINDOW_UTILITY : 0; sdl_flags |= (viewport->Flags & ImGuiViewportFlags_TopMost) ? SDL_WINDOW_ALWAYS_ON_TOP : 0; vd->Window = SDL_CreateWindow("No Title Yet", (int)viewport->Size.x, (int)viewport->Size.y, sdl_flags); + vd->WindowID = SDL_GetWindowID(vd->Window); #ifndef __APPLE__ // On Mac, SDL3 Parenting appears to prevent viewport from appearing in another monitor SDL_SetWindowParent(vd->Window, vd->ParentWindow); #endif @@ -1121,6 +1127,8 @@ static void ImGui_ImplSDL3_DestroyWindow(ImGuiViewport* viewport) { if (ImGui_ImplSDL3_ViewportData* vd = (ImGui_ImplSDL3_ViewportData*)viewport->PlatformUserData) { + ImGui_ImplSDL3_Data* bd = ImGui_ImplSDL3_GetBackendData(); + bd->LastFrameDestroyedWindowIDs.push_back(vd->WindowID); if (vd->GLContext && vd->WindowOwned) SDL_GL_DestroyContext(vd->GLContext); if (vd->Window && vd->WindowOwned) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 31dc01125..b133abf9a 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -111,6 +111,8 @@ Docking+Viewports Branch: - Fixed an issue where in some code paths, a newly created modal in its own viewport would be created with `ImGuiViewportFlags_NoFocusOnAppearing`. - Backends: + - SDL2, SDL3: on macOS: fixed stuck down keys/buttons when they were held while thee + focused viewport gets destroyed (Regression on macOS in 1.91.1) (#9121, #7853) - Win32: make windows appear with `::SetWindowPos()` w/ `SWP_NOOWNERZORDER` instead of unparenting + `ShowWindow()`. Fixes missing taskbar icon for newly appearing windows that are parented and use `ImGuiViewportFlags_NoFocusOnAppearing`. (#7354, #8669)