diff --git a/backends/imgui_impl_glfw.cpp b/backends/imgui_impl_glfw.cpp index 6e2e7fde..dfc81e09 100644 --- a/backends/imgui_impl_glfw.cpp +++ b/backends/imgui_impl_glfw.cpp @@ -55,7 +55,7 @@ #define GLFW_HAS_WINDOW_ALPHA (GLFW_VERSION_MAJOR * 1000 + GLFW_VERSION_MINOR * 100 >= 3300) // 3.3+ glfwSetWindowOpacity #define GLFW_HAS_PER_MONITOR_DPI (GLFW_VERSION_MAJOR * 1000 + GLFW_VERSION_MINOR * 100 >= 3300) // 3.3+ glfwGetMonitorContentScale #define GLFW_HAS_VULKAN (GLFW_VERSION_MAJOR * 1000 + GLFW_VERSION_MINOR * 100 >= 3200) // 3.2+ glfwCreateWindowSurface -#ifdef GLFW_RESIZE_NESW_CURSOR // let's be nice to people who pulled GLFW between 2019-04-16 (3.4 define) and 2019-11-29 (cursors defines) // FIXME: Remove when GLFW 3.4 is released? +#ifdef GLFW_RESIZE_NESW_CURSOR // Let's be nice to people who pulled GLFW between 2019-04-16 (3.4 define) and 2019-11-29 (cursors defines) // FIXME: Remove when GLFW 3.4 is released? #define GLFW_HAS_NEW_CURSORS (GLFW_VERSION_MAJOR * 1000 + GLFW_VERSION_MINOR * 100 >= 3400) // 3.4+ GLFW_RESIZE_ALL_CURSOR, GLFW_RESIZE_NESW_CURSOR, GLFW_RESIZE_NWSE_CURSOR, GLFW_NOT_ALLOWED_CURSOR #else #define GLFW_HAS_NEW_CURSORS (0) @@ -116,7 +116,7 @@ static void ImGui_ImplGlfw_SetClipboardText(void* user_data, const char* text) void ImGui_ImplGlfw_MouseButtonCallback(GLFWwindow* window, int button, int action, int mods) { ImGui_ImplGlfw_Data* bd = ImGui_ImplGlfw_GetBackendData(); - if (bd->PrevUserCallbackMousebutton != NULL) + if (bd->PrevUserCallbackMousebutton != NULL && window == bd->Window) bd->PrevUserCallbackMousebutton(window, button, action, mods); if (action == GLFW_PRESS && button >= 0 && button < IM_ARRAYSIZE(bd->MouseJustPressed)) @@ -126,7 +126,7 @@ void ImGui_ImplGlfw_MouseButtonCallback(GLFWwindow* window, int button, int acti void ImGui_ImplGlfw_ScrollCallback(GLFWwindow* window, double xoffset, double yoffset) { ImGui_ImplGlfw_Data* bd = ImGui_ImplGlfw_GetBackendData(); - if (bd->PrevUserCallbackScroll != NULL) + if (bd->PrevUserCallbackScroll != NULL && window == bd->Window) bd->PrevUserCallbackScroll(window, xoffset, yoffset); ImGuiIO& io = ImGui::GetIO(); @@ -137,7 +137,7 @@ void ImGui_ImplGlfw_ScrollCallback(GLFWwindow* window, double xoffset, double yo void ImGui_ImplGlfw_KeyCallback(GLFWwindow* window, int key, int scancode, int action, int mods) { ImGui_ImplGlfw_Data* bd = ImGui_ImplGlfw_GetBackendData(); - if (bd->PrevUserCallbackKey != NULL) + if (bd->PrevUserCallbackKey != NULL && window == bd->Window) bd->PrevUserCallbackKey(window, key, scancode, action, mods); ImGuiIO& io = ImGui::GetIO(); @@ -174,7 +174,7 @@ 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 != NULL) + if (bd->PrevUserCallbackChar != NULL && window == bd->Window) bd->PrevUserCallbackChar(window, c); ImGuiIO& io = ImGui::GetIO(); @@ -298,6 +298,7 @@ void ImGui_ImplGlfw_Shutdown() if (bd->InstalledCallbacks) { + glfwSetCursorEnterCallback(bd->Window, bd->PrevUserCallbackCursorEnter); glfwSetMouseButtonCallback(bd->Window, bd->PrevUserCallbackMousebutton); glfwSetScrollCallback(bd->Window, bd->PrevUserCallbackScroll); glfwSetKeyCallback(bd->Window, bd->PrevUserCallbackKey); @@ -316,32 +317,32 @@ void ImGui_ImplGlfw_Shutdown() static void ImGui_ImplGlfw_UpdateMousePosAndButtons() { ImGui_ImplGlfw_Data* bd = ImGui_ImplGlfw_GetBackendData(); - - // Update buttons ImGuiIO& io = ImGui::GetIO(); + + const ImVec2 mouse_pos_prev = io.MousePos; + io.MousePos = ImVec2(-FLT_MAX, -FLT_MAX); + + // Update mouse buttons + // (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) 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] = bd->MouseJustPressed[i] || glfwGetMouseButton(bd->Window, i) != 0; bd->MouseJustPressed[i] = false; } #ifdef __EMSCRIPTEN__ - const bool focused = true; // Emscripten + const bool focused = true; #else const bool focused = glfwGetWindowAttrib(bd->Window, GLFW_FOCUSED) != 0; #endif GLFWwindow* mouse_window = (bd->MouseWindow == bd->Window || focused) ? bd->Window : NULL; - // Update mouse position - const ImVec2 mouse_pos_backup = io.MousePos; - io.MousePos = ImVec2(-FLT_MAX, -FLT_MAX); - if (io.WantSetMousePos) - { - if (focused) - glfwSetCursorPos(bd->Window, (double)mouse_pos_backup.x, (double)mouse_pos_backup.y); - } - else if (mouse_window != NULL) + // Set OS mouse position from Dear ImGui if requested (rarely used, only when ImGuiConfigFlags_NavEnableSetMousePos is enabled by user) + if (io.WantSetMousePos && focused) + glfwSetCursorPos(bd->Window, (double)mouse_pos_prev.x, (double)mouse_pos_prev.y); + + // Set Dear ImGui mouse position from OS position + if (mouse_window != NULL) { double mouse_x, mouse_y; glfwGetCursorPos(mouse_window, &mouse_x, &mouse_y); diff --git a/backends/imgui_impl_sdl.cpp b/backends/imgui_impl_sdl.cpp index 6589279f..09e6acb6 100644 --- a/backends/imgui_impl_sdl.cpp +++ b/backends/imgui_impl_sdl.cpp @@ -59,10 +59,11 @@ #include "TargetConditionals.h" #endif -#define SDL_HAS_CAPTURE_AND_GLOBAL_MOUSE SDL_VERSION_ATLEAST(2,0,4) +#define SDL_HAS_CAPTURE_AND_GLOBAL_MOUSE (SDL_VERSION_ATLEAST(2,0,4) && !defined(__EMSCRIPTEN__) && !defined(__ANDROID__) && !(defined(__APPLE__) && TARGET_OS_IOS)) #define SDL_HAS_MOUSE_FOCUS_CLICKTHROUGH SDL_VERSION_ATLEAST(2,0,5) #define SDL_HAS_VULKAN SDL_VERSION_ATLEAST(2,0,6) +// SDL Data struct ImGui_ImplSDL2_Data { SDL_Window* Window; @@ -156,6 +157,17 @@ static bool ImGui_ImplSDL2_Init(SDL_Window* window) ImGuiIO& io = ImGui::GetIO(); IM_ASSERT(io.BackendPlatformUserData == NULL && "Already initialized a platform backend!"); + // Check and store if we are on a SDL backend that supports global mouse position + // ("wayland" and "rpi" don't support it, but we chose to use a white-list instead of a black-list) + const char* sdl_backend = SDL_GetCurrentVideoDriver(); + const char* global_mouse_whitelist[] = { "windows", "cocoa", "x11", "DIVE", "VMAN" }; + bool mouse_can_use_global_state = false; +#if SDL_HAS_CAPTURE_AND_GLOBAL_MOUSE + for (int n = 0; n < IM_ARRAYSIZE(global_mouse_whitelist); n++) + if (strncmp(sdl_backend, global_mouse_whitelist[n], strlen(global_mouse_whitelist[n])) == 0) + mouse_can_use_global_state = true; +#endif + // Setup backend capabilities flags ImGui_ImplSDL2_Data* bd = IM_NEW(ImGui_ImplSDL2_Data)(); io.BackendPlatformUserData = (void*)bd; @@ -164,6 +176,7 @@ static bool ImGui_ImplSDL2_Init(SDL_Window* window) io.BackendFlags |= ImGuiBackendFlags_HasSetMousePos; // We can honor io.WantSetMousePos requests (optional, rarely used) bd->Window = window; + bd->MouseCanUseGlobalState = mouse_can_use_global_state; // Keyboard mapping. Dear ImGui will use those indices to peek into the io.KeysDown[] array. io.KeyMap[ImGuiKey_Tab] = SDL_SCANCODE_TAB; @@ -204,20 +217,11 @@ static bool ImGui_ImplSDL2_Init(SDL_Window* window) bd->MouseCursors[ImGuiMouseCursor_Hand] = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_HAND); bd->MouseCursors[ImGuiMouseCursor_NotAllowed] = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_NO); - // Check and store if we are on a SDL backend that supports global mouse position - // ("wayland" and "rpi" don't support it, but we chose to use a white-list instead of a black-list) - const char* sdl_backend = SDL_GetCurrentVideoDriver(); - const char* global_mouse_whitelist[] = { "windows", "cocoa", "x11", "DIVE", "VMAN" }; - bd->MouseCanUseGlobalState = false; - for (int n = 0; n < IM_ARRAYSIZE(global_mouse_whitelist); n++) - if (strncmp(sdl_backend, global_mouse_whitelist[n], strlen(global_mouse_whitelist[n])) == 0) - bd->MouseCanUseGlobalState = true; - #ifdef _WIN32 - SDL_SysWMinfo wmInfo; - SDL_VERSION(&wmInfo.version); - SDL_GetWindowWMInfo(window, &wmInfo); - io.ImeWindowHandle = wmInfo.info.win.window; + SDL_SysWMinfo info; + SDL_VERSION(&info.version); + if (SDL_GetWindowWMInfo(window, &info)) + io.ImeWindowHandle = info.info.win.window; #else (void)window; #endif @@ -278,55 +282,59 @@ void ImGui_ImplSDL2_Shutdown() static void ImGui_ImplSDL2_UpdateMousePosAndButtons() { - ImGuiIO& io = ImGui::GetIO(); ImGui_ImplSDL2_Data* bd = ImGui_ImplSDL2_GetBackendData(); + ImGuiIO& io = ImGui::GetIO(); - // Set OS mouse position 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); - else - io.MousePos = ImVec2(-FLT_MAX, -FLT_MAX); + ImVec2 mouse_pos_prev = io.MousePos; + io.MousePos = ImVec2(-FLT_MAX, -FLT_MAX); - int mx, my; - Uint32 mouse_buttons = SDL_GetMouseState(&mx, &my); + // Update mouse buttons + int mouse_x_local, mouse_y_local; + Uint32 mouse_buttons = SDL_GetMouseState(&mouse_x_local, &mouse_y_local); 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; -#if SDL_HAS_CAPTURE_AND_GLOBAL_MOUSE && !defined(__EMSCRIPTEN__) && !defined(__ANDROID__) && !(defined(__APPLE__) && TARGET_OS_IOS) - SDL_Window* focused_window = SDL_GetKeyboardFocus(); // Mouse position won't be reported unless window is focused. -#if SDL_HAS_MOUSE_FOCUS_CLICKTHROUGH - SDL_Window* hovered_window = SDL_GetMouseFocus(); // This is better but is only reliably useful with SDL 2.0.5+ and SDL_HINT_MOUSE_FOCUS_CLICKTHROUGH enabled. - SDL_Window* mouse_window = (bd->Window == focused_window || bd->Window == hovered_window) ? bd->Window : NULL; + // 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(ImGui::IsAnyMouseDown() ? SDL_TRUE : SDL_FALSE); #else - SDL_Window* mouse_window = (bd->Window == focused_window) ? bd->Window : NULL; + // 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) + + 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) { - if (bd->MouseCanUseGlobalState) - { - // SDL_GetMouseState() gives mouse position seemingly based on the last window entered/focused(?) - // The creation of a new windows at runtime and SDL_CaptureMouse both seems to severely mess up with that, so we retrieve that position globally. - // Won't use this workaround on SDL backends that have no global mouse position, like Wayland or RPI - int wx, wy; - SDL_GetWindowPosition(mouse_window, &wx, &wy); - SDL_GetGlobalMouseState(&mx, &my); - mx -= wx; - my -= wy; - } - io.MousePos = ImVec2((float)mx, (float)my); + // 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); } - - // SDL_CaptureMouse() let the OS know e.g. that our imgui drag outside the SDL window boundaries shouldn't e.g. trigger the OS window resize cursor. - // The function is only supported from SDL 2.0.4 (released Jan 2016) - bool any_mouse_button_down = ImGui::IsAnyMouseDown(); - SDL_CaptureMouse(any_mouse_button_down ? SDL_TRUE : SDL_FALSE); -#else - // SDL 2.0.3 and non-windowed systems - if (SDL_GetWindowFlags(bd->Window) & SDL_WINDOW_INPUT_FOCUS) - io.MousePos = ImVec2((float)mx, (float)my); -#endif } static void ImGui_ImplSDL2_UpdateMouseCursor() @@ -393,9 +401,9 @@ static void ImGui_ImplSDL2_UpdateGamepads() void ImGui_ImplSDL2_NewFrame() { - ImGuiIO& io = ImGui::GetIO(); ImGui_ImplSDL2_Data* bd = ImGui_ImplSDL2_GetBackendData(); IM_ASSERT(bd != NULL && "Did you call ImGui_ImplSDL2_Init()?"); + ImGuiIO& io = ImGui::GetIO(); // Setup display size (every frame to accommodate for window resizing) int w, h; diff --git a/backends/imgui_impl_win32.cpp b/backends/imgui_impl_win32.cpp index 3953e32e..4124b974 100644 --- a/backends/imgui_impl_win32.cpp +++ b/backends/imgui_impl_win32.cpp @@ -219,31 +219,36 @@ static bool ImGui_ImplWin32_UpdateMouseCursor() static void ImGui_ImplWin32_UpdateMousePos() { - ImGuiIO& io = ImGui::GetIO(); ImGui_ImplWin32_Data* bd = ImGui_ImplWin32_GetBackendData(); + ImGuiIO& io = ImGui::GetIO(); IM_ASSERT(bd->hWnd != 0); - // Set OS mouse position if requested (rarely used, only when ImGuiConfigFlags_NavEnableSetMousePos is enabled by user) - if (io.WantSetMousePos) - { - POINT pos = { (int)io.MousePos.x, (int)io.MousePos.y }; - if (::ClientToScreen(bd->hWnd, &pos)) - ::SetCursorPos(pos.x, pos.y); - } - - // Set mouse position + const ImVec2 mouse_pos_prev = io.MousePos; io.MousePos = ImVec2(-FLT_MAX, -FLT_MAX); + + // Obtain focused and hovered window. We forward mouse input when focused or when hovered (and no other window is capturing) HWND focused_window = ::GetForegroundWindow(); HWND hovered_window = bd->MouseHwnd; HWND mouse_window = NULL; - if (hovered_window && (hovered_window == bd->hWnd) || ::IsChild(hovered_window, bd->hWnd)) + if (hovered_window && (hovered_window == bd->hWnd || ::IsChild(hovered_window, bd->hWnd))) mouse_window = hovered_window; - else if (focused_window && (focused_window == bd->hWnd) || ::IsChild(focused_window, bd->hWnd)) + else if (focused_window && (focused_window == bd->hWnd || ::IsChild(focused_window, bd->hWnd))) mouse_window = focused_window; + 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) + { + POINT pos = { (int)mouse_pos_prev.x, (int)mouse_pos_prev.y }; + if (::ClientToScreen(bd->hWnd, &pos)) + ::SetCursorPos(pos.x, pos.y); + } + + // Set Dear ImGui mouse position from OS position POINT pos; - if (mouse_window) - if (::GetCursorPos(&pos) && ::ScreenToClient(mouse_window, &pos)) - io.MousePos = ImVec2((float)pos.x, (float)pos.y); + if (::GetCursorPos(&pos) && ::ScreenToClient(mouse_window, &pos)) + io.MousePos = ImVec2((float)pos.x, (float)pos.y); } // Gamepad navigation mapping