diff --git a/backends/imgui_impl_glfw.cpp b/backends/imgui_impl_glfw.cpp index cd0f51dbcdb1..319b48bb4251 100644 --- a/backends/imgui_impl_glfw.cpp +++ b/backends/imgui_impl_glfw.cpp @@ -38,6 +38,7 @@ // 2016-10-15: Misc: Added a void* user_data parameter to Clipboard function handlers. #include "imgui.h" +#include "imgui_internal.h" #include "imgui_impl_glfw.h" // GLFW @@ -58,25 +59,48 @@ #define GLFW_HAS_NEW_CURSORS (0) #endif -// Data enum GlfwClientApi { GlfwClientApi_Unknown, GlfwClientApi_OpenGL, GlfwClientApi_Vulkan }; -static GLFWwindow* g_Window = NULL; // Main window -static GlfwClientApi g_ClientApi = GlfwClientApi_Unknown; -static double g_Time = 0.0; -static bool g_MouseJustPressed[ImGuiMouseButton_COUNT] = {}; -static GLFWcursor* g_MouseCursors[ImGuiMouseCursor_COUNT] = {}; -static bool g_InstalledCallbacks = false; - -// Chain GLFW callbacks: our callbacks will call the user's previously installed callbacks, if any. -static GLFWmousebuttonfun g_PrevUserCallbackMousebutton = NULL; -static GLFWscrollfun g_PrevUserCallbackScroll = NULL; -static GLFWkeyfun g_PrevUserCallbackKey = NULL; -static GLFWcharfun g_PrevUserCallbackChar = NULL; + +// State for each GLFWWindow +struct State { + GlfwClientApi ClientApi = GlfwClientApi_Unknown; + double Time = 0.0; + bool MouseJustPressed[ImGuiMouseButton_COUNT] = {}; + GLFWcursor* MouseCursors[ImGuiMouseCursor_COUNT] = {}; + bool InstalledCallbacks = false; + + // Chain GLFW callbacks: our callbacks will call the user's previously installed callbacks, if any. + GLFWmousebuttonfun PrevUserCallbackMousebutton = NULL; + GLFWscrollfun PrevUserCallbackScroll = NULL; + GLFWkeyfun PrevUserCallbackKey = NULL; + GLFWcharfun PrevUserCallbackChar = NULL; +}; + +// Map GLFWwindow pointer to State +static ImGuiStorage g_StateStorage; + +// The initial window given to the first call to ImGui_ImplGlfw_Init. Fall +// back to this window if another window isn't provided in ImGui_ImplGlfw_Shutdown or ImGui_ImplGlfw_NewFrame +static GLFWwindow* g_DefaultWindow = NULL; + +static State* ImGui_ImplGlfw_InitWindowState(GLFWwindow* window) +{ + State* state = IM_NEW(State)(); + const ImGuiID window_hash = ImHashData(&window, sizeof(void*), 0); + g_StateStorage.SetVoidPtr(window_hash, state); + return state; +} + +static State* ImGui_ImplGlfw_GetWindowState(GLFWwindow* window) +{ + const ImGuiID window_hash = ImHashData(&window, sizeof(void*), 0); + return (State*)g_StateStorage.GetVoidPtr(window_hash); +} static const char* ImGui_ImplGlfw_GetClipboardText(void* user_data) { @@ -90,17 +114,21 @@ static void ImGui_ImplGlfw_SetClipboardText(void* user_data, const char* text) void ImGui_ImplGlfw_MouseButtonCallback(GLFWwindow* window, int button, int action, int mods) { - if (g_PrevUserCallbackMousebutton != NULL) - g_PrevUserCallbackMousebutton(window, button, action, mods); + State* state = ImGui_ImplGlfw_GetWindowState(window); + + if (state->PrevUserCallbackMousebutton != NULL) + state->PrevUserCallbackMousebutton(window, button, action, mods); - if (action == GLFW_PRESS && button >= 0 && button < IM_ARRAYSIZE(g_MouseJustPressed)) - g_MouseJustPressed[button] = true; + if (action == GLFW_PRESS && button >= 0 && button < IM_ARRAYSIZE(state->MouseJustPressed)) + state->MouseJustPressed[button] = true; } void ImGui_ImplGlfw_ScrollCallback(GLFWwindow* window, double xoffset, double yoffset) { - if (g_PrevUserCallbackScroll != NULL) - g_PrevUserCallbackScroll(window, xoffset, yoffset); + State* state = ImGui_ImplGlfw_GetWindowState(window); + + if (state->PrevUserCallbackScroll != NULL) + state->PrevUserCallbackScroll(window, xoffset, yoffset); ImGuiIO& io = ImGui::GetIO(); io.MouseWheelH += (float)xoffset; @@ -109,8 +137,10 @@ void ImGui_ImplGlfw_ScrollCallback(GLFWwindow* window, double xoffset, double yo void ImGui_ImplGlfw_KeyCallback(GLFWwindow* window, int key, int scancode, int action, int mods) { - if (g_PrevUserCallbackKey != NULL) - g_PrevUserCallbackKey(window, key, scancode, action, mods); + State* state = ImGui_ImplGlfw_GetWindowState(window); + + if (state->PrevUserCallbackKey != NULL) + state->PrevUserCallbackKey(window, key, scancode, action, mods); ImGuiIO& io = ImGui::GetIO(); if (action == GLFW_PRESS) @@ -131,8 +161,10 @@ void ImGui_ImplGlfw_KeyCallback(GLFWwindow* window, int key, int scancode, int a void ImGui_ImplGlfw_CharCallback(GLFWwindow* window, unsigned int c) { - if (g_PrevUserCallbackChar != NULL) - g_PrevUserCallbackChar(window, c); + State* state = ImGui_ImplGlfw_GetWindowState(window); + + if (state->PrevUserCallbackChar != NULL) + state->PrevUserCallbackChar(window, c); ImGuiIO& io = ImGui::GetIO(); io.AddInputCharacter(c); @@ -140,8 +172,12 @@ void ImGui_ImplGlfw_CharCallback(GLFWwindow* window, unsigned int c) static bool ImGui_ImplGlfw_Init(GLFWwindow* window, bool install_callbacks, GlfwClientApi client_api) { - g_Window = window; - g_Time = 0.0; + if (g_DefaultWindow == NULL) + g_DefaultWindow = window; + + State* state = ImGui_ImplGlfw_InitWindowState(window); + + state->Time = 0.0; // Setup backend capabilities flags ImGuiIO& io = ImGui::GetIO(); @@ -175,9 +211,9 @@ static bool ImGui_ImplGlfw_Init(GLFWwindow* window, bool install_callbacks, Glfw io.SetClipboardTextFn = ImGui_ImplGlfw_SetClipboardText; io.GetClipboardTextFn = ImGui_ImplGlfw_GetClipboardText; - io.ClipboardUserData = g_Window; + io.ClipboardUserData = window; #if defined(_WIN32) - io.ImeWindowHandle = (void*)glfwGetWin32Window(g_Window); + io.ImeWindowHandle = (void*)glfwGetWin32Window(window); #endif // Create mouse cursors @@ -185,39 +221,39 @@ static bool ImGui_ImplGlfw_Init(GLFWwindow* window, bool install_callbacks, Glfw // GLFW will emit an error which will often be printed by the app, so we temporarily disable error reporting. // Missing cursors will return NULL and our _UpdateMouseCursor() function will use the Arrow cursor instead.) GLFWerrorfun prev_error_callback = glfwSetErrorCallback(NULL); - g_MouseCursors[ImGuiMouseCursor_Arrow] = glfwCreateStandardCursor(GLFW_ARROW_CURSOR); - g_MouseCursors[ImGuiMouseCursor_TextInput] = glfwCreateStandardCursor(GLFW_IBEAM_CURSOR); - g_MouseCursors[ImGuiMouseCursor_ResizeNS] = glfwCreateStandardCursor(GLFW_VRESIZE_CURSOR); - g_MouseCursors[ImGuiMouseCursor_ResizeEW] = glfwCreateStandardCursor(GLFW_HRESIZE_CURSOR); - g_MouseCursors[ImGuiMouseCursor_Hand] = glfwCreateStandardCursor(GLFW_HAND_CURSOR); + state->MouseCursors[ImGuiMouseCursor_Arrow] = glfwCreateStandardCursor(GLFW_ARROW_CURSOR); + state->MouseCursors[ImGuiMouseCursor_TextInput] = glfwCreateStandardCursor(GLFW_IBEAM_CURSOR); + state->MouseCursors[ImGuiMouseCursor_ResizeNS] = glfwCreateStandardCursor(GLFW_VRESIZE_CURSOR); + state->MouseCursors[ImGuiMouseCursor_ResizeEW] = glfwCreateStandardCursor(GLFW_HRESIZE_CURSOR); + state->MouseCursors[ImGuiMouseCursor_Hand] = glfwCreateStandardCursor(GLFW_HAND_CURSOR); #if GLFW_HAS_NEW_CURSORS - g_MouseCursors[ImGuiMouseCursor_ResizeAll] = glfwCreateStandardCursor(GLFW_RESIZE_ALL_CURSOR); - g_MouseCursors[ImGuiMouseCursor_ResizeNESW] = glfwCreateStandardCursor(GLFW_RESIZE_NESW_CURSOR); - g_MouseCursors[ImGuiMouseCursor_ResizeNWSE] = glfwCreateStandardCursor(GLFW_RESIZE_NWSE_CURSOR); - g_MouseCursors[ImGuiMouseCursor_NotAllowed] = glfwCreateStandardCursor(GLFW_NOT_ALLOWED_CURSOR); + state->MouseCursors[ImGuiMouseCursor_ResizeAll] = glfwCreateStandardCursor(GLFW_RESIZE_ALL_CURSOR); + state->MouseCursors[ImGuiMouseCursor_ResizeNESW] = glfwCreateStandardCursor(GLFW_RESIZE_NESW_CURSOR); + state->MouseCursors[ImGuiMouseCursor_ResizeNWSE] = glfwCreateStandardCursor(GLFW_RESIZE_NWSE_CURSOR); + state->MouseCursors[ImGuiMouseCursor_NotAllowed] = glfwCreateStandardCursor(GLFW_NOT_ALLOWED_CURSOR); #else - g_MouseCursors[ImGuiMouseCursor_ResizeAll] = glfwCreateStandardCursor(GLFW_ARROW_CURSOR); - g_MouseCursors[ImGuiMouseCursor_ResizeNESW] = glfwCreateStandardCursor(GLFW_ARROW_CURSOR); - g_MouseCursors[ImGuiMouseCursor_ResizeNWSE] = glfwCreateStandardCursor(GLFW_ARROW_CURSOR); - g_MouseCursors[ImGuiMouseCursor_NotAllowed] = glfwCreateStandardCursor(GLFW_ARROW_CURSOR); + state->MouseCursors[ImGuiMouseCursor_ResizeAll] = glfwCreateStandardCursor(GLFW_ARROW_CURSOR); + state->MouseCursors[ImGuiMouseCursor_ResizeNESW] = glfwCreateStandardCursor(GLFW_ARROW_CURSOR); + state->MouseCursors[ImGuiMouseCursor_ResizeNWSE] = glfwCreateStandardCursor(GLFW_ARROW_CURSOR); + state->MouseCursors[ImGuiMouseCursor_NotAllowed] = glfwCreateStandardCursor(GLFW_ARROW_CURSOR); #endif glfwSetErrorCallback(prev_error_callback); // Chain GLFW callbacks: our callbacks will call the user's previously installed callbacks, if any. - g_PrevUserCallbackMousebutton = NULL; - g_PrevUserCallbackScroll = NULL; - g_PrevUserCallbackKey = NULL; - g_PrevUserCallbackChar = NULL; + state->PrevUserCallbackMousebutton = NULL; + state->PrevUserCallbackScroll = NULL; + state->PrevUserCallbackKey = NULL; + state->PrevUserCallbackChar = NULL; if (install_callbacks) { - g_InstalledCallbacks = true; - g_PrevUserCallbackMousebutton = glfwSetMouseButtonCallback(window, ImGui_ImplGlfw_MouseButtonCallback); - g_PrevUserCallbackScroll = glfwSetScrollCallback(window, ImGui_ImplGlfw_ScrollCallback); - g_PrevUserCallbackKey = glfwSetKeyCallback(window, ImGui_ImplGlfw_KeyCallback); - g_PrevUserCallbackChar = glfwSetCharCallback(window, ImGui_ImplGlfw_CharCallback); + state->InstalledCallbacks = true; + state->PrevUserCallbackMousebutton = glfwSetMouseButtonCallback(window, ImGui_ImplGlfw_MouseButtonCallback); + state->PrevUserCallbackScroll = glfwSetScrollCallback(window, ImGui_ImplGlfw_ScrollCallback); + state->PrevUserCallbackKey = glfwSetKeyCallback(window, ImGui_ImplGlfw_KeyCallback); + state->PrevUserCallbackChar = glfwSetCharCallback(window, ImGui_ImplGlfw_CharCallback); } - g_ClientApi = client_api; + state->ClientApi = client_api; return true; } @@ -236,34 +272,43 @@ bool ImGui_ImplGlfw_InitForOther(GLFWwindow* window, bool install_callbacks) return ImGui_ImplGlfw_Init(window, install_callbacks, GlfwClientApi_Unknown); } -void ImGui_ImplGlfw_Shutdown() +void ImGui_ImplGlfw_Shutdown(GLFWwindow* window) { - if (g_InstalledCallbacks) + if (window == NULL) + window = g_DefaultWindow; + + State* state = ImGui_ImplGlfw_GetWindowState(window); + + if (state->InstalledCallbacks) { - glfwSetMouseButtonCallback(g_Window, g_PrevUserCallbackMousebutton); - glfwSetScrollCallback(g_Window, g_PrevUserCallbackScroll); - glfwSetKeyCallback(g_Window, g_PrevUserCallbackKey); - glfwSetCharCallback(g_Window, g_PrevUserCallbackChar); - g_InstalledCallbacks = false; + glfwSetMouseButtonCallback(window, state->PrevUserCallbackMousebutton); + glfwSetScrollCallback(window, state->PrevUserCallbackScroll); + glfwSetKeyCallback(window, state->PrevUserCallbackKey); + glfwSetCharCallback(window, state->PrevUserCallbackChar); + state->InstalledCallbacks = false; } for (ImGuiMouseCursor cursor_n = 0; cursor_n < ImGuiMouseCursor_COUNT; cursor_n++) { - glfwDestroyCursor(g_MouseCursors[cursor_n]); - g_MouseCursors[cursor_n] = NULL; + glfwDestroyCursor(state->MouseCursors[cursor_n]); + state->MouseCursors[cursor_n] = NULL; } - g_ClientApi = GlfwClientApi_Unknown; + state->ClientApi = GlfwClientApi_Unknown; + + IM_DELETE(state); } -static void ImGui_ImplGlfw_UpdateMousePosAndButtons() +static void ImGui_ImplGlfw_UpdateMousePosAndButtons(GLFWwindow* window) { + State* state = ImGui_ImplGlfw_GetWindowState(window); + // Update buttons ImGuiIO& io = ImGui::GetIO(); for (int i = 0; i < IM_ARRAYSIZE(io.MouseDown); i++) { // 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[i] = g_MouseJustPressed[i] || glfwGetMouseButton(g_Window, i) != 0; - g_MouseJustPressed[i] = false; + io.MouseDown[i] = state->MouseJustPressed[i] || glfwGetMouseButton(window, i) != 0; + state->MouseJustPressed[i] = false; } // Update mouse position @@ -272,41 +317,43 @@ static void ImGui_ImplGlfw_UpdateMousePosAndButtons() #ifdef __EMSCRIPTEN__ const bool focused = true; // Emscripten #else - const bool focused = glfwGetWindowAttrib(g_Window, GLFW_FOCUSED) != 0; + const bool focused = glfwGetWindowAttrib(window, GLFW_FOCUSED) != 0; #endif if (focused) { if (io.WantSetMousePos) { - glfwSetCursorPos(g_Window, (double)mouse_pos_backup.x, (double)mouse_pos_backup.y); + glfwSetCursorPos(window, (double)mouse_pos_backup.x, (double)mouse_pos_backup.y); } else { double mouse_x, mouse_y; - glfwGetCursorPos(g_Window, &mouse_x, &mouse_y); + glfwGetCursorPos(window, &mouse_x, &mouse_y); io.MousePos = ImVec2((float)mouse_x, (float)mouse_y); } } } -static void ImGui_ImplGlfw_UpdateMouseCursor() +static void ImGui_ImplGlfw_UpdateMouseCursor(GLFWwindow* window) { + State* state = ImGui_ImplGlfw_GetWindowState(window); + ImGuiIO& io = ImGui::GetIO(); - if ((io.ConfigFlags & ImGuiConfigFlags_NoMouseCursorChange) || glfwGetInputMode(g_Window, GLFW_CURSOR) == GLFW_CURSOR_DISABLED) + if ((io.ConfigFlags & ImGuiConfigFlags_NoMouseCursorChange) || glfwGetInputMode(window, GLFW_CURSOR) == GLFW_CURSOR_DISABLED) return; ImGuiMouseCursor imgui_cursor = ImGui::GetMouseCursor(); if (imgui_cursor == ImGuiMouseCursor_None || io.MouseDrawCursor) { // Hide OS mouse cursor if imgui is drawing it or if it wants no cursor - glfwSetInputMode(g_Window, GLFW_CURSOR, GLFW_CURSOR_HIDDEN); + glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_HIDDEN); } else { // Show OS mouse cursor // FIXME-PLATFORM: Unfocused windows seems to fail changing the mouse cursor with GLFW 3.2, but 3.3 works here. - glfwSetCursor(g_Window, g_MouseCursors[imgui_cursor] ? g_MouseCursors[imgui_cursor] : g_MouseCursors[ImGuiMouseCursor_Arrow]); - glfwSetInputMode(g_Window, GLFW_CURSOR, GLFW_CURSOR_NORMAL); + glfwSetCursor(window, state->MouseCursors[imgui_cursor] ? state->MouseCursors[imgui_cursor] : state->MouseCursors[ImGuiMouseCursor_Arrow]); + glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_NORMAL); } } @@ -347,27 +394,32 @@ static void ImGui_ImplGlfw_UpdateGamepads() io.BackendFlags &= ~ImGuiBackendFlags_HasGamepad; } -void ImGui_ImplGlfw_NewFrame() +void ImGui_ImplGlfw_NewFrame(GLFWwindow* window) { ImGuiIO& io = ImGui::GetIO(); IM_ASSERT(io.Fonts->IsBuilt() && "Font atlas not built! It is generally built by the renderer backend. Missing call to renderer _NewFrame() function? e.g. ImGui_ImplOpenGL3_NewFrame()."); + if (window == NULL) + window = g_DefaultWindow; + + State* state = ImGui_ImplGlfw_GetWindowState(window); + // Setup display size (every frame to accommodate for window resizing) int w, h; int display_w, display_h; - glfwGetWindowSize(g_Window, &w, &h); - glfwGetFramebufferSize(g_Window, &display_w, &display_h); + glfwGetWindowSize(window, &w, &h); + glfwGetFramebufferSize(window, &display_w, &display_h); io.DisplaySize = ImVec2((float)w, (float)h); if (w > 0 && h > 0) io.DisplayFramebufferScale = ImVec2((float)display_w / w, (float)display_h / h); // Setup time step double current_time = glfwGetTime(); - io.DeltaTime = g_Time > 0.0 ? (float)(current_time - g_Time) : (float)(1.0f / 60.0f); - g_Time = current_time; + io.DeltaTime = state->Time > 0.0 ? (float)(current_time - state->Time) : (float)(1.0f / 60.0f); + state->Time = current_time; - ImGui_ImplGlfw_UpdateMousePosAndButtons(); - ImGui_ImplGlfw_UpdateMouseCursor(); + ImGui_ImplGlfw_UpdateMousePosAndButtons(window); + ImGui_ImplGlfw_UpdateMouseCursor(window); // Update game controllers (if enabled and available) ImGui_ImplGlfw_UpdateGamepads(); diff --git a/backends/imgui_impl_glfw.h b/backends/imgui_impl_glfw.h index 018f1a1ee6c3..4d9c5da536fe 100644 --- a/backends/imgui_impl_glfw.h +++ b/backends/imgui_impl_glfw.h @@ -24,8 +24,8 @@ struct GLFWwindow; IMGUI_IMPL_API bool ImGui_ImplGlfw_InitForOpenGL(GLFWwindow* window, bool install_callbacks); IMGUI_IMPL_API bool ImGui_ImplGlfw_InitForVulkan(GLFWwindow* window, bool install_callbacks); IMGUI_IMPL_API bool ImGui_ImplGlfw_InitForOther(GLFWwindow* window, bool install_callbacks); -IMGUI_IMPL_API void ImGui_ImplGlfw_Shutdown(); -IMGUI_IMPL_API void ImGui_ImplGlfw_NewFrame(); +IMGUI_IMPL_API void ImGui_ImplGlfw_Shutdown(GLFWwindow* window = NULL); +IMGUI_IMPL_API void ImGui_ImplGlfw_NewFrame(GLFWwindow* window = NULL); // GLFW callbacks // - When calling Init with 'install_callbacks=true': GLFW callbacks will be installed for you. They will call user's previously installed callbacks, if any.