From c56e8b496467aa573ca0f65a295b4b1235718d43 Mon Sep 17 00:00:00 2001 From: ocornut Date: Wed, 18 Jun 2025 15:31:00 +0200 Subject: [PATCH 1/3] imgui_freetype: fixed NULL that creeped in instead of nullptr. --- misc/freetype/imgui_freetype.cpp | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/misc/freetype/imgui_freetype.cpp b/misc/freetype/imgui_freetype.cpp index d7d50db39..e5f2818db 100644 --- a/misc/freetype/imgui_freetype.cpp +++ b/misc/freetype/imgui_freetype.cpp @@ -346,7 +346,7 @@ static void* FreeType_Realloc(FT_Memory /*memory*/, long cur_size, long new_size bool ImGui_ImplFreeType_LoaderInit(ImFontAtlas* atlas) { - IM_ASSERT(atlas->FontLoaderData == NULL); + IM_ASSERT(atlas->FontLoaderData == nullptr); ImGui_ImplFreeType_Data* bd = IM_NEW(ImGui_ImplFreeType_Data)(); // FreeType memory management: https://www.freetype.org/freetype2/docs/design/design-4.html @@ -387,23 +387,23 @@ bool ImGui_ImplFreeType_LoaderInit(ImFontAtlas* atlas) void ImGui_ImplFreeType_LoaderShutdown(ImFontAtlas* atlas) { ImGui_ImplFreeType_Data* bd = (ImGui_ImplFreeType_Data*)atlas->FontLoaderData; - IM_ASSERT(bd != NULL); + IM_ASSERT(bd != nullptr); FT_Done_Library(bd->Library); IM_DELETE(bd); - atlas->FontLoaderData = NULL; + atlas->FontLoaderData = nullptr; } bool ImGui_ImplFreeType_FontSrcInit(ImFontAtlas* atlas, ImFontConfig* src) { ImGui_ImplFreeType_Data* bd = (ImGui_ImplFreeType_Data*)atlas->FontLoaderData; ImGui_ImplFreeType_FontSrcData* bd_font_data = IM_NEW(ImGui_ImplFreeType_FontSrcData); - IM_ASSERT(src->FontLoaderData == NULL); + IM_ASSERT(src->FontLoaderData == nullptr); src->FontLoaderData = bd_font_data; if (!bd_font_data->InitFont(bd->Library, src, (ImGuiFreeTypeLoaderFlags)atlas->FontLoaderFlags)) { IM_DELETE(bd_font_data); - src->FontLoaderData = NULL; + src->FontLoaderData = nullptr; return false; } @@ -415,7 +415,7 @@ void ImGui_ImplFreeType_FontSrcDestroy(ImFontAtlas* atlas, ImFontConfig* src) IM_UNUSED(atlas); ImGui_ImplFreeType_FontSrcData* bd_font_data = (ImGui_ImplFreeType_FontSrcData*)src->FontLoaderData; IM_DELETE(bd_font_data); - src->FontLoaderData = NULL; + src->FontLoaderData = nullptr; } bool ImGui_ImplFreeType_FontBakedInit(ImFontAtlas* atlas, ImFontConfig* src, ImFontBaked* baked, void* loader_data_for_baked_src) @@ -430,7 +430,7 @@ bool ImGui_ImplFreeType_FontBakedInit(ImFontAtlas* atlas, ImFontConfig* src, ImF // We use one FT_Size per (source + baked) combination. ImGui_ImplFreeType_FontSrcBakedData* bd_baked_data = (ImGui_ImplFreeType_FontSrcBakedData*)loader_data_for_baked_src; - IM_ASSERT(bd_baked_data != NULL); + IM_ASSERT(bd_baked_data != nullptr); IM_PLACEMENT_NEW(bd_baked_data) ImGui_ImplFreeType_FontSrcBakedData(); FT_New_Size(bd_font_data->FtFace, &bd_baked_data->FtSize); @@ -470,7 +470,7 @@ void ImGui_ImplFreeType_FontBakedDestroy(ImFontAtlas* atlas, ImFontConfig* src, IM_UNUSED(baked); IM_UNUSED(src); ImGui_ImplFreeType_FontSrcBakedData* bd_baked_data = (ImGui_ImplFreeType_FontSrcBakedData*)loader_data_for_baked_src; - IM_ASSERT(bd_baked_data != NULL); + IM_ASSERT(bd_baked_data != nullptr); FT_Done_Size(bd_baked_data->FtSize); bd_baked_data->~ImGui_ImplFreeType_FontSrcBakedData(); // ~IM_PLACEMENT_DELETE() } @@ -491,7 +491,7 @@ bool ImGui_ImplFreeType_FontBakedLoadGlyph(ImFontAtlas* atlas, ImFontConfig* src } const FT_Glyph_Metrics* metrics = ImGui_ImplFreeType_LoadGlyph(bd_font_data, codepoint); - if (metrics == NULL) + if (metrics == nullptr) return false; // Render glyph into a bitmap (currently held by FreeType) From d290e583c54dcb020cc96cfcab2f629441c7dad5 Mon Sep 17 00:00:00 2001 From: ocornut Date: Wed, 18 Jun 2025 15:50:49 +0200 Subject: [PATCH 2/3] Backends: GLFW: fixed WndProc relying on current context. (#8676, #8239, #8069) This makes the backend closer to support multi-context. --- backends/imgui_impl_glfw.cpp | 16 +++++++++++++--- docs/CHANGELOG.txt | 1 + 2 files changed, 14 insertions(+), 3 deletions(-) diff --git a/backends/imgui_impl_glfw.cpp b/backends/imgui_impl_glfw.cpp index aac9eca2e..fb8df3e55 100644 --- a/backends/imgui_impl_glfw.cpp +++ b/backends/imgui_impl_glfw.cpp @@ -191,6 +191,10 @@ static ImGui_ImplGlfw_Data* ImGui_ImplGlfw_GetBackendData() { return ImGui::GetCurrentContext() ? (ImGui_ImplGlfw_Data*)ImGui::GetIO().BackendPlatformUserData : nullptr; } +static ImGui_ImplGlfw_Data* ImGui_ImplGlfw_GetBackendData(ImGuiIO& io) +{ + return (ImGui_ImplGlfw_Data*)io.BackendPlatformUserData; +} // Functions @@ -501,6 +505,7 @@ static EM_BOOL ImGui_ImplEmscripten_WheelCallback(int, const EmscriptenWheelEven #ifdef _WIN32 // GLFW doesn't allow to distinguish Mouse vs TouchScreen vs Pen. // Add support for Win32 (based on imgui_impl_win32), because we rely on _TouchScreen info to trickle inputs differently. +namespace ImGui { extern ImGuiIO& GetIO(ImGuiContext*); } static ImGuiMouseSource GetMouseSourceFromMessageExtraInfo() { LPARAM extra_info = ::GetMessageExtraInfo(); @@ -512,7 +517,10 @@ static ImGuiMouseSource GetMouseSourceFromMessageExtraInfo() } static LRESULT CALLBACK ImGui_ImplGlfw_WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) { - ImGui_ImplGlfw_Data* bd = ImGui_ImplGlfw_GetBackendData(); + ImGuiContext* ctx = (ImGuiContext*)::GetPropA(hWnd, "IMGUI_CONTEXT"); + ImGuiIO& io = ImGui::GetIO(ctx); + + ImGui_ImplGlfw_Data* bd = ImGui_ImplGlfw_GetBackendData(io); switch (msg) { case WM_MOUSEMOVE: case WM_NCMOUSEMOVE: @@ -520,7 +528,7 @@ static LRESULT CALLBACK ImGui_ImplGlfw_WndProc(HWND hWnd, UINT msg, WPARAM wPara case WM_RBUTTONDOWN: case WM_RBUTTONDBLCLK: case WM_RBUTTONUP: case WM_MBUTTONDOWN: case WM_MBUTTONDBLCLK: case WM_MBUTTONUP: case WM_XBUTTONDOWN: case WM_XBUTTONDBLCLK: case WM_XBUTTONUP: - ImGui::GetIO().AddMouseSourceEvent(GetMouseSourceFromMessageExtraInfo()); + io.AddMouseSourceEvent(GetMouseSourceFromMessageExtraInfo()); break; } return ::CallWindowProcW(bd->PrevWndProc, hWnd, msg, wParam, lParam); @@ -661,6 +669,7 @@ static bool ImGui_ImplGlfw_Init(GLFWwindow* window, bool install_callbacks, Glfw // Windows: register a WndProc hook so we can intercept some messages. #ifdef _WIN32 + ::SetPropA((HWND)main_viewport->PlatformHandleRaw, "IMGUI_CONTEXT", ImGui::GetCurrentContext()); bd->PrevWndProc = (WNDPROC)::GetWindowLongPtrW((HWND)main_viewport->PlatformHandleRaw, GWLP_WNDPROC); IM_ASSERT(bd->PrevWndProc != nullptr); ::SetWindowLongPtrW((HWND)main_viewport->PlatformHandleRaw, GWLP_WNDPROC, (LONG_PTR)ImGui_ImplGlfw_WndProc); @@ -672,7 +681,7 @@ static bool ImGui_ImplGlfw_Init(GLFWwindow* window, bool install_callbacks, Glfw #if EMSCRIPTEN_USE_PORT_CONTRIB_GLFW3 >= 34020240817 if (emscripten::glfw3::IsRuntimePlatformApple()) { - ImGui::GetIO().ConfigMacOSXBehaviors = true; + io.ConfigMacOSXBehaviors = true; // Due to how the browser (poorly) handles the Meta Key, this line essentially disables repeats when used. // This means that Meta + V only registers a single key-press, even if the keys are held. @@ -721,6 +730,7 @@ void ImGui_ImplGlfw_Shutdown() // Windows: restore our WndProc hook #ifdef _WIN32 ImGuiViewport* main_viewport = ImGui::GetMainViewport(); + ::SetPropA((HWND)main_viewport->PlatformHandleRaw, "IMGUI_CONTEXT", nullptr); ::SetWindowLongPtrW((HWND)main_viewport->PlatformHandleRaw, GWLP_WNDPROC, (LONG_PTR)bd->PrevWndProc); bd->PrevWndProc = nullptr; #endif diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 2104b9c9c..72edad331 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -407,6 +407,7 @@ Other changes: - Backends: GLFW: added ImGui_ImplGlfw_GetContentScaleForMonitor(), ImGui_ImplGlfw_GetContentScaleForWindow() helpers. They are wrappers to glfwGetMonitorContentScale()/glfwGetWindowContentScale(), with compile-time GLFW version checks + returning 1.0f on Apple platform. + - Backends: GLFW: fixed Win32 specific WndProc handler relying on current context. (#8676, #8239, #8069) - Backends: SDL2: added ImGui_ImplSDL2_GetDpiScaleForDisplay() and ImGui_ImplSDL2_GetContentScaleForWindow() helpers. They are wrappers to SDL_GetDisplayDPI(), with compile-time SDL version checks + returning 1.0f on Apple platforms. SDL3 already does this by default. From f633a60581bc7573f76c9913f66695aa02caf5d0 Mon Sep 17 00:00:00 2001 From: ocornut Date: Wed, 18 Jun 2025 16:40:35 +0200 Subject: [PATCH 3/3] Backends: GLFW: Added support for multiple Dear ImGui contexts. (#8676, #8239, #8069) --- backends/imgui_impl_glfw.cpp | 102 ++++++++++++++++++++--------------- backends/imgui_impl_glfw.h | 1 + docs/CHANGELOG.txt | 2 +- 3 files changed, 62 insertions(+), 43 deletions(-) diff --git a/backends/imgui_impl_glfw.cpp b/backends/imgui_impl_glfw.cpp index fb8df3e55..5518a823e 100644 --- a/backends/imgui_impl_glfw.cpp +++ b/backends/imgui_impl_glfw.cpp @@ -9,6 +9,7 @@ // [X] Platform: Keyboard support. Since 1.87 we are using the io.AddKeyEvent() function. Pass ImGuiKey values to all key functions e.g. ImGui::IsKeyPressed(ImGuiKey_Space). [Legacy GLFW_KEY_* values are obsolete since 1.87 and not supported since 1.91.5] // [X] Platform: Gamepad support. Enable with 'io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad'. // [X] Platform: Mouse cursor shape and visibility (ImGuiBackendFlags_HasMouseCursors). Resizing cursors requires GLFW 3.4+! Disable with 'io.ConfigFlags |= ImGuiConfigFlags_NoMouseCursorChange'. +// [X] Multiple Dear ImGui contexts support. // Missing features or Issues: // [ ] Touch events are only correctly identified as Touch on Windows. This create issues with some interactions. GLFW doesn't provide a way to identify touch inputs from mouse inputs, we cannot call io.AddMouseSourceEvent() to identify the source. We provide a Windows-specific workaround. // [ ] Missing ImGuiMouseCursor_Wait and ImGuiMouseCursor_Progress cursors. @@ -28,6 +29,7 @@ // CHANGELOG // (minor and older changes stripped away, please see git history for details) +// 2025-06-18: Added support for multiple Dear ImGui contexts. (#8676, #8239, #8069) // 2025-06-11: Added ImGui_ImplGlfw_GetContentScaleForWindow(GLFWwindow* window) and ImGui_ImplGlfw_GetContentScaleForMonitor(GLFWmonitor* monitor) helper to facilitate making DPI-aware apps. // 2025-03-10: Map GLFW_KEY_WORLD_1 and GLFW_KEY_WORLD_2 into ImGuiKey_Oem102. // 2025-03-03: Fixed clipboard handler assertion when using GLFW <= 3.2.1 compiled with asserts enabled. @@ -141,6 +143,16 @@ #define GLFW_HAS_GETKEYNAME (GLFW_VERSION_COMBINED >= 3200) // 3.2+ glfwGetKeyName() #define GLFW_HAS_GETERROR (GLFW_VERSION_COMBINED >= 3300) // 3.3+ glfwGetError() +// Map GLFWWindow* to ImGuiContext*. +// - Would be simpler if we could use glfwSetWindowUserPointer()/glfwGetWindowUserPointer(), but this is a single and shared resource. +// - Would be simpler if we could use e.g. std::map<> as well. But we don't. +// - This is not particularly optimized as we expect size to be small and queries to be rare. +struct ImGui_ImplGlfw_WindowToContext { GLFWwindow* Window; ImGuiContext* Context; }; +static ImVector g_ContextMap; +static void ImGui_ImplGlfw_ContextMap_Add(GLFWwindow* window, ImGuiContext* ctx) { g_ContextMap.push_back(ImGui_ImplGlfw_WindowToContext{ window, ctx }); } +static void ImGui_ImplGlfw_ContextMap_Remove(GLFWwindow* window) { for (ImGui_ImplGlfw_WindowToContext& entry : g_ContextMap) if (entry.Window == window) { g_ContextMap.erase_unsorted(&entry); return; } } +static ImGuiContext* ImGui_ImplGlfw_ContextMap_Get(GLFWwindow* window) { for (ImGui_ImplGlfw_WindowToContext& entry : g_ContextMap) if (entry.Window == window) return entry.Context; return nullptr; } + // GLFW data enum GlfwClientApi { @@ -151,6 +163,7 @@ enum GlfwClientApi struct ImGui_ImplGlfw_Data { + ImGuiContext* Context; GLFWwindow* Window; GlfwClientApi ClientApi; double Time; @@ -187,13 +200,17 @@ struct ImGui_ImplGlfw_Data // (passing install_callbacks=false in ImGui_ImplGlfw_InitXXX functions), set the current dear imgui context and then call our callbacks. // - Otherwise we may need to store a GLFWWindow* -> ImGuiContext* map and handle this in the backend, adding a little bit of extra complexity to it. // FIXME: some shared resources (mouse cursor shape, gamepad) are mishandled when using multi-context. +namespace ImGui { extern ImGuiIO& GetIO(ImGuiContext*); } static ImGui_ImplGlfw_Data* ImGui_ImplGlfw_GetBackendData() { + // Get data for current context return ImGui::GetCurrentContext() ? (ImGui_ImplGlfw_Data*)ImGui::GetIO().BackendPlatformUserData : nullptr; } -static ImGui_ImplGlfw_Data* ImGui_ImplGlfw_GetBackendData(ImGuiIO& io) +static ImGui_ImplGlfw_Data* ImGui_ImplGlfw_GetBackendData(GLFWwindow* window) { - return (ImGui_ImplGlfw_Data*)io.BackendPlatformUserData; + // Get data for a given GLFW window, regardless of current context (since GLFW events are sent together) + ImGuiContext* ctx = ImGui_ImplGlfw_ContextMap_Get(window); + return (ImGui_ImplGlfw_Data*)ImGui::GetIO(ctx).BackendPlatformUserData; } // Functions @@ -330,38 +347,36 @@ ImGuiKey ImGui_ImplGlfw_KeyToImGuiKey(int keycode, int scancode) // X11 does not include current pressed/released modifier key in 'mods' flags submitted by GLFW // See https://github.com/ocornut/imgui/issues/6034 and https://github.com/glfw/glfw/issues/1630 -static void ImGui_ImplGlfw_UpdateKeyModifiers(GLFWwindow* window) +static void ImGui_ImplGlfw_UpdateKeyModifiers(ImGuiIO& io, GLFWwindow* window) { - ImGuiIO& io = ImGui::GetIO(); io.AddKeyEvent(ImGuiMod_Ctrl, (glfwGetKey(window, GLFW_KEY_LEFT_CONTROL) == GLFW_PRESS) || (glfwGetKey(window, GLFW_KEY_RIGHT_CONTROL) == GLFW_PRESS)); io.AddKeyEvent(ImGuiMod_Shift, (glfwGetKey(window, GLFW_KEY_LEFT_SHIFT) == GLFW_PRESS) || (glfwGetKey(window, GLFW_KEY_RIGHT_SHIFT) == GLFW_PRESS)); io.AddKeyEvent(ImGuiMod_Alt, (glfwGetKey(window, GLFW_KEY_LEFT_ALT) == GLFW_PRESS) || (glfwGetKey(window, GLFW_KEY_RIGHT_ALT) == GLFW_PRESS)); io.AddKeyEvent(ImGuiMod_Super, (glfwGetKey(window, GLFW_KEY_LEFT_SUPER) == GLFW_PRESS) || (glfwGetKey(window, GLFW_KEY_RIGHT_SUPER) == GLFW_PRESS)); } -static bool ImGui_ImplGlfw_ShouldChainCallback(GLFWwindow* window) +static bool ImGui_ImplGlfw_ShouldChainCallback(ImGui_ImplGlfw_Data* bd, GLFWwindow* window) { - ImGui_ImplGlfw_Data* bd = ImGui_ImplGlfw_GetBackendData(); return bd->CallbacksChainForAllWindows ? true : (window == bd->Window); } void ImGui_ImplGlfw_MouseButtonCallback(GLFWwindow* window, int button, int action, int mods) { - ImGui_ImplGlfw_Data* bd = ImGui_ImplGlfw_GetBackendData(); - if (bd->PrevUserCallbackMousebutton != nullptr && ImGui_ImplGlfw_ShouldChainCallback(window)) + ImGui_ImplGlfw_Data* bd = ImGui_ImplGlfw_GetBackendData(window); + + if (bd->PrevUserCallbackMousebutton != nullptr && ImGui_ImplGlfw_ShouldChainCallback(bd, window)) bd->PrevUserCallbackMousebutton(window, button, action, mods); - ImGui_ImplGlfw_UpdateKeyModifiers(window); - - ImGuiIO& io = ImGui::GetIO(); + ImGuiIO& io = ImGui::GetIO(bd->Context); + ImGui_ImplGlfw_UpdateKeyModifiers(io, window); if (button >= 0 && button < ImGuiMouseButton_COUNT) io.AddMouseButtonEvent(button, action == GLFW_PRESS); } void ImGui_ImplGlfw_ScrollCallback(GLFWwindow* window, double xoffset, double yoffset) { - ImGui_ImplGlfw_Data* bd = ImGui_ImplGlfw_GetBackendData(); - if (bd->PrevUserCallbackScroll != nullptr && ImGui_ImplGlfw_ShouldChainCallback(window)) + ImGui_ImplGlfw_Data* bd = ImGui_ImplGlfw_GetBackendData(window); + if (bd->PrevUserCallbackScroll != nullptr && ImGui_ImplGlfw_ShouldChainCallback(bd, window)) bd->PrevUserCallbackScroll(window, xoffset, yoffset); #ifdef EMSCRIPTEN_USE_EMBEDDED_GLFW3 @@ -369,7 +384,7 @@ void ImGui_ImplGlfw_ScrollCallback(GLFWwindow* window, double xoffset, double yo return; #endif - ImGuiIO& io = ImGui::GetIO(); + ImGuiIO& io = ImGui::GetIO(bd->Context); io.AddMouseWheelEvent((float)xoffset, (float)yoffset); } @@ -409,18 +424,18 @@ static int ImGui_ImplGlfw_TranslateUntranslatedKey(int key, int scancode) void ImGui_ImplGlfw_KeyCallback(GLFWwindow* window, int keycode, int scancode, int action, int mods) { - ImGui_ImplGlfw_Data* bd = ImGui_ImplGlfw_GetBackendData(); - if (bd->PrevUserCallbackKey != nullptr && ImGui_ImplGlfw_ShouldChainCallback(window)) + ImGui_ImplGlfw_Data* bd = ImGui_ImplGlfw_GetBackendData(window); + if (bd->PrevUserCallbackKey != nullptr && ImGui_ImplGlfw_ShouldChainCallback(bd, window)) bd->PrevUserCallbackKey(window, keycode, scancode, action, mods); if (action != GLFW_PRESS && action != GLFW_RELEASE) return; - ImGui_ImplGlfw_UpdateKeyModifiers(window); + ImGuiIO& io = ImGui::GetIO(bd->Context); + ImGui_ImplGlfw_UpdateKeyModifiers(io, window); keycode = ImGui_ImplGlfw_TranslateUntranslatedKey(keycode, scancode); - ImGuiIO& io = ImGui::GetIO(); ImGuiKey imgui_key = ImGui_ImplGlfw_KeyToImGuiKey(keycode, scancode); io.AddKeyEvent(imgui_key, (action == GLFW_PRESS)); io.SetKeyEventNativeData(imgui_key, keycode, scancode); // To support legacy indexing (<1.87 user code) @@ -428,21 +443,21 @@ void ImGui_ImplGlfw_KeyCallback(GLFWwindow* window, int keycode, int scancode, i void ImGui_ImplGlfw_WindowFocusCallback(GLFWwindow* window, int focused) { - ImGui_ImplGlfw_Data* bd = ImGui_ImplGlfw_GetBackendData(); - if (bd->PrevUserCallbackWindowFocus != nullptr && ImGui_ImplGlfw_ShouldChainCallback(window)) + ImGui_ImplGlfw_Data* bd = ImGui_ImplGlfw_GetBackendData(window); + if (bd->PrevUserCallbackWindowFocus != nullptr && ImGui_ImplGlfw_ShouldChainCallback(bd, window)) bd->PrevUserCallbackWindowFocus(window, focused); - ImGuiIO& io = ImGui::GetIO(); + ImGuiIO& io = ImGui::GetIO(bd->Context); io.AddFocusEvent(focused != 0); } void ImGui_ImplGlfw_CursorPosCallback(GLFWwindow* window, double x, double y) { - ImGui_ImplGlfw_Data* bd = ImGui_ImplGlfw_GetBackendData(); - if (bd->PrevUserCallbackCursorPos != nullptr && ImGui_ImplGlfw_ShouldChainCallback(window)) + ImGui_ImplGlfw_Data* bd = ImGui_ImplGlfw_GetBackendData(window); + if (bd->PrevUserCallbackCursorPos != nullptr && ImGui_ImplGlfw_ShouldChainCallback(bd, window)) bd->PrevUserCallbackCursorPos(window, x, y); - ImGuiIO& io = ImGui::GetIO(); + ImGuiIO& io = ImGui::GetIO(bd->Context); io.AddMousePosEvent((float)x, (float)y); bd->LastValidMousePos = ImVec2((float)x, (float)y); } @@ -451,11 +466,11 @@ void ImGui_ImplGlfw_CursorPosCallback(GLFWwindow* window, double x, double y) // so we back it up and restore on Leave/Enter (see https://github.com/ocornut/imgui/issues/4984) void ImGui_ImplGlfw_CursorEnterCallback(GLFWwindow* window, int entered) { - ImGui_ImplGlfw_Data* bd = ImGui_ImplGlfw_GetBackendData(); - if (bd->PrevUserCallbackCursorEnter != nullptr && ImGui_ImplGlfw_ShouldChainCallback(window)) + ImGui_ImplGlfw_Data* bd = ImGui_ImplGlfw_GetBackendData(window); + if (bd->PrevUserCallbackCursorEnter != nullptr && ImGui_ImplGlfw_ShouldChainCallback(bd, window)) bd->PrevUserCallbackCursorEnter(window, entered); - ImGuiIO& io = ImGui::GetIO(); + ImGuiIO& io = ImGui::GetIO(bd->Context); if (entered) { bd->MouseWindow = window; @@ -471,11 +486,11 @@ void ImGui_ImplGlfw_CursorEnterCallback(GLFWwindow* window, int entered) void ImGui_ImplGlfw_CharCallback(GLFWwindow* window, unsigned int c) { - ImGui_ImplGlfw_Data* bd = ImGui_ImplGlfw_GetBackendData(); - if (bd->PrevUserCallbackChar != nullptr && ImGui_ImplGlfw_ShouldChainCallback(window)) + ImGui_ImplGlfw_Data* bd = ImGui_ImplGlfw_GetBackendData(window); + if (bd->PrevUserCallbackChar != nullptr && ImGui_ImplGlfw_ShouldChainCallback(bd, window)) bd->PrevUserCallbackChar(window, c); - ImGuiIO& io = ImGui::GetIO(); + ImGuiIO& io = ImGui::GetIO(bd->Context); io.AddInputCharacter(c); } @@ -485,17 +500,18 @@ void ImGui_ImplGlfw_MonitorCallback(GLFWmonitor*, int) } #ifdef EMSCRIPTEN_USE_EMBEDDED_GLFW3 -static EM_BOOL ImGui_ImplEmscripten_WheelCallback(int, const EmscriptenWheelEvent* ev, void*) +static EM_BOOL ImGui_ImplEmscripten_WheelCallback(int, const EmscriptenWheelEvent* ev, void* user_data) { // Mimic Emscripten_HandleWheel() in SDL. // Corresponding equivalent in GLFW JS emulation layer has incorrect quantizing preventing small values. See #6096 + ImGui_ImplGlfw_Data* bd = (ImGui_ImplGlfw_Data*)user_data; float multiplier = 0.0f; if (ev->deltaMode == DOM_DELTA_PIXEL) { multiplier = 1.0f / 100.0f; } // 100 pixels make up a step. else if (ev->deltaMode == DOM_DELTA_LINE) { multiplier = 1.0f / 3.0f; } // 3 lines make up a step. else if (ev->deltaMode == DOM_DELTA_PAGE) { multiplier = 80.0f; } // A page makes up 80 steps. float wheel_x = ev->deltaX * -multiplier; float wheel_y = ev->deltaY * -multiplier; - ImGuiIO& io = ImGui::GetIO(); + ImGuiIO& io = ImGui::GetIO(bd->Context); io.AddMouseWheelEvent(wheel_x, wheel_y); //IMGUI_DEBUG_LOG("[Emsc] mode %d dx: %.2f, dy: %.2f, dz: %.2f --> feed %.2f %.2f\n", (int)ev->deltaMode, ev->deltaX, ev->deltaY, ev->deltaZ, wheel_x, wheel_y); return EM_TRUE; @@ -505,7 +521,6 @@ static EM_BOOL ImGui_ImplEmscripten_WheelCallback(int, const EmscriptenWheelEven #ifdef _WIN32 // GLFW doesn't allow to distinguish Mouse vs TouchScreen vs Pen. // Add support for Win32 (based on imgui_impl_win32), because we rely on _TouchScreen info to trickle inputs differently. -namespace ImGui { extern ImGuiIO& GetIO(ImGuiContext*); } static ImGuiMouseSource GetMouseSourceFromMessageExtraInfo() { LPARAM extra_info = ::GetMessageExtraInfo(); @@ -517,10 +532,9 @@ static ImGuiMouseSource GetMouseSourceFromMessageExtraInfo() } static LRESULT CALLBACK ImGui_ImplGlfw_WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) { - ImGuiContext* ctx = (ImGuiContext*)::GetPropA(hWnd, "IMGUI_CONTEXT"); - ImGuiIO& io = ImGui::GetIO(ctx); + ImGui_ImplGlfw_Data* bd = (ImGui_ImplGlfw_Data*)::GetPropA(hWnd, "IMGUI_BACKEND_DATA"); + ImGuiIO& io = ImGui::GetIO(bd->Context); - ImGui_ImplGlfw_Data* bd = ImGui_ImplGlfw_GetBackendData(io); switch (msg) { case WM_MOUSEMOVE: case WM_NCMOUSEMOVE: @@ -537,7 +551,7 @@ static LRESULT CALLBACK ImGui_ImplGlfw_WndProc(HWND hWnd, UINT msg, WPARAM wPara void ImGui_ImplGlfw_InstallCallbacks(GLFWwindow* window) { - ImGui_ImplGlfw_Data* bd = ImGui_ImplGlfw_GetBackendData(); + ImGui_ImplGlfw_Data* bd = ImGui_ImplGlfw_GetBackendData(window); IM_ASSERT(bd->InstalledCallbacks == false && "Callbacks already installed!"); IM_ASSERT(bd->Window == window); @@ -554,7 +568,7 @@ void ImGui_ImplGlfw_InstallCallbacks(GLFWwindow* window) void ImGui_ImplGlfw_RestoreCallbacks(GLFWwindow* window) { - ImGui_ImplGlfw_Data* bd = ImGui_ImplGlfw_GetBackendData(); + ImGui_ImplGlfw_Data* bd = ImGui_ImplGlfw_GetBackendData(window); IM_ASSERT(bd->InstalledCallbacks == true && "Callbacks not installed!"); IM_ASSERT(bd->Window == window); @@ -610,8 +624,10 @@ static bool ImGui_ImplGlfw_Init(GLFWwindow* window, bool install_callbacks, Glfw io.BackendFlags |= ImGuiBackendFlags_HasMouseCursors; // We can honor GetMouseCursor() values (optional) io.BackendFlags |= ImGuiBackendFlags_HasSetMousePos; // We can honor io.WantSetMousePos requests (optional, rarely used) + bd->Context = ImGui::GetCurrentContext(); bd->Window = window; bd->Time = 0.0; + ImGui_ImplGlfw_ContextMap_Add(window, bd->Context); ImGuiPlatformIO& platform_io = ImGui::GetPlatformIO(); #if GLFW_VERSION_COMBINED < 3300 @@ -669,8 +685,9 @@ static bool ImGui_ImplGlfw_Init(GLFWwindow* window, bool install_callbacks, Glfw // Windows: register a WndProc hook so we can intercept some messages. #ifdef _WIN32 - ::SetPropA((HWND)main_viewport->PlatformHandleRaw, "IMGUI_CONTEXT", ImGui::GetCurrentContext()); - bd->PrevWndProc = (WNDPROC)::GetWindowLongPtrW((HWND)main_viewport->PlatformHandleRaw, GWLP_WNDPROC); + HWND hwnd = (HWND)main_viewport->PlatformHandleRaw; + ::SetPropA(hwnd, "IMGUI_BACKEND_DATA", bd); + bd->PrevWndProc = (WNDPROC)::GetWindowLongPtrW(hwnd, GWLP_WNDPROC); IM_ASSERT(bd->PrevWndProc != nullptr); ::SetWindowLongPtrW((HWND)main_viewport->PlatformHandleRaw, GWLP_WNDPROC, (LONG_PTR)ImGui_ImplGlfw_WndProc); #endif @@ -730,7 +747,7 @@ void ImGui_ImplGlfw_Shutdown() // Windows: restore our WndProc hook #ifdef _WIN32 ImGuiViewport* main_viewport = ImGui::GetMainViewport(); - ::SetPropA((HWND)main_viewport->PlatformHandleRaw, "IMGUI_CONTEXT", nullptr); + ::SetPropA((HWND)main_viewport->PlatformHandleRaw, "IMGUI_BACKEND_DATA", nullptr); ::SetWindowLongPtrW((HWND)main_viewport->PlatformHandleRaw, GWLP_WNDPROC, (LONG_PTR)bd->PrevWndProc); bd->PrevWndProc = nullptr; #endif @@ -738,6 +755,7 @@ void ImGui_ImplGlfw_Shutdown() io.BackendPlatformName = nullptr; io.BackendPlatformUserData = nullptr; io.BackendFlags &= ~(ImGuiBackendFlags_HasMouseCursors | ImGuiBackendFlags_HasSetMousePos | ImGuiBackendFlags_HasGamepad); + ImGui_ImplGlfw_ContextMap_Remove(bd->Window); IM_DELETE(bd); } @@ -961,7 +979,7 @@ void ImGui_ImplGlfw_InstallEmscriptenCallbacks(GLFWwindow*, const char* canvas_s // Register Emscripten Wheel callback to workaround issue in Emscripten GLFW Emulation (#6096) // We intentionally do not check 'if (install_callbacks)' here, as some users may set it to false and call GLFW callback themselves. // FIXME: May break chaining in case user registered their own Emscripten callback? - emscripten_set_wheel_callback(bd->CanvasSelector, nullptr, false, ImGui_ImplEmscripten_WheelCallback); + emscripten_set_wheel_callback(bd->CanvasSelector, bd, false, ImGui_ImplEmscripten_WheelCallback); } #elif defined(EMSCRIPTEN_USE_PORT_CONTRIB_GLFW3) // When using --use-port=contrib.glfw3 for the GLFW implementation, you can override the behavior of this call diff --git a/backends/imgui_impl_glfw.h b/backends/imgui_impl_glfw.h index 1ef9ade17..80e2b55ef 100644 --- a/backends/imgui_impl_glfw.h +++ b/backends/imgui_impl_glfw.h @@ -8,6 +8,7 @@ // [X] Platform: Keyboard support. Since 1.87 we are using the io.AddKeyEvent() function. Pass ImGuiKey values to all key functions e.g. ImGui::IsKeyPressed(ImGuiKey_Space). [Legacy GLFW_KEY_* values are obsolete since 1.87 and not supported since 1.91.5] // [X] Platform: Gamepad support. Enable with 'io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad'. // [X] Platform: Mouse cursor shape and visibility (ImGuiBackendFlags_HasMouseCursors). Resizing cursors requires GLFW 3.4+! Disable with 'io.ConfigFlags |= ImGuiConfigFlags_NoMouseCursorChange'. +// [X] Multiple Dear ImGui contexts support. // Missing features or Issues: // [ ] Touch events are only correctly identified as Touch on Windows. This create issues with some interactions. GLFW doesn't provide a way to identify touch inputs from mouse inputs, we cannot call io.AddMouseSourceEvent() to identify the source. We provide a Windows-specific workaround. // [ ] Missing ImGuiMouseCursor_Wait and ImGuiMouseCursor_Progress cursors. diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 72edad331..b6f5c9e6e 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -407,7 +407,7 @@ Other changes: - Backends: GLFW: added ImGui_ImplGlfw_GetContentScaleForMonitor(), ImGui_ImplGlfw_GetContentScaleForWindow() helpers. They are wrappers to glfwGetMonitorContentScale()/glfwGetWindowContentScale(), with compile-time GLFW version checks + returning 1.0f on Apple platform. - - Backends: GLFW: fixed Win32 specific WndProc handler relying on current context. (#8676, #8239, #8069) + - Backends: GLFW: Added support for multiple Dear ImGui contexts. (#8676, #8239, #8069) - Backends: SDL2: added ImGui_ImplSDL2_GetDpiScaleForDisplay() and ImGui_ImplSDL2_GetContentScaleForWindow() helpers. They are wrappers to SDL_GetDisplayDPI(), with compile-time SDL version checks + returning 1.0f on Apple platforms. SDL3 already does this by default.