From d574604a5de563eb60a990f5117de0ba488e3cb9 Mon Sep 17 00:00:00 2001 From: omar Date: Fri, 11 May 2018 14:05:40 +0200 Subject: [PATCH] Viewport, Platform: Win32: Fixed handling of io.WantSetMousePos + added a bunch of comments. GLFW, SDL2: Added handling of io.WantSetMousePos. (#1542) --- examples/imgui_impl_glfw.cpp | 22 ++++++------ examples/imgui_impl_sdl2.cpp | 17 +++++++++ examples/imgui_impl_win32.cpp | 65 +++++++++++++++++++---------------- 3 files changed, 64 insertions(+), 40 deletions(-) diff --git a/examples/imgui_impl_glfw.cpp b/examples/imgui_impl_glfw.cpp index a8e9c8c8..88f61d1e 100644 --- a/examples/imgui_impl_glfw.cpp +++ b/examples/imgui_impl_glfw.cpp @@ -120,7 +120,7 @@ static bool ImGui_ImplGlfw_Init(GLFWwindow* window, bool install_callbacks, Glfw // Setup back-end capabilities flags ImGuiIO& io = ImGui::GetIO(); io.BackendFlags |= ImGuiBackendFlags_HasMouseCursors; // We can honor GetMouseCursor() values (optional) - //io.BackendFlags |= ImGuiBackendFlags_HasSetMousePos; // We can honor io.WantSetMousePos requests (optional, rarely used) + io.BackendFlags |= ImGuiBackendFlags_HasSetMousePos; // We can honor io.WantSetMousePos requests (optional, rarely used) io.BackendFlags |= ImGuiBackendFlags_PlatformHasViewports; // We can create multi-viewports on the Platform side (optional) #if GLFW_HAS_GLFW_HOVERED io.BackendFlags |= ImGuiBackendFlags_HasMouseHoveredViewport; // We can set io.MouseHoveredViewport correctly (optional, not easy) @@ -198,13 +198,8 @@ void ImGui_ImplGlfw_Shutdown() static void ImGui_ImplGlfw_UpdateMouse() { -#if 0 - // Set OS mouse position if requested (only used when ImGuiConfigFlags_NavEnableSetMousePos is enabled by user) - if (io.WantSetMousePos) - glfwSetCursorPos(g_Window, (double)io.MousePos.x, (double)io.MousePos.y); -#endif - ImGuiIO& io = ImGui::GetIO(); + const ImVec2 mouse_pos_backup = io.MousePos; io.MousePos = ImVec2(-FLT_MAX, -FLT_MAX); io.MousePosViewport = 0; io.MouseHoveredViewport = 0; @@ -225,9 +220,16 @@ static void ImGui_ImplGlfw_UpdateMouse() IM_ASSERT(window != NULL); if (glfwGetWindowAttrib(window, GLFW_FOCUSED)) { - double mouse_x, mouse_y; - glfwGetCursorPos(window, &mouse_x, &mouse_y); - io.MousePos = ImVec2((float)mouse_x + viewport->Pos.x, (float)mouse_y + viewport->Pos.y); + if (io.WantSetMousePos) + { + glfwSetCursorPos(window, (double)(mouse_pos_backup.x - viewport->Pos.x), (double)(mouse_pos_backup.y - viewport->Pos.y)); + } + else + { + double mouse_x, mouse_y; + glfwGetCursorPos(window, &mouse_x, &mouse_y); + io.MousePos = ImVec2((float)mouse_x + viewport->Pos.x, (float)mouse_y + viewport->Pos.y); + } io.MousePosViewport = viewport->ID; for (int i = 0; i < IM_ARRAYSIZE(io.MouseDown); i++) io.MouseDown[i] |= glfwGetMouseButton(window, i) != 0; diff --git a/examples/imgui_impl_sdl2.cpp b/examples/imgui_impl_sdl2.cpp index 8234fcf8..5d153249 100644 --- a/examples/imgui_impl_sdl2.cpp +++ b/examples/imgui_impl_sdl2.cpp @@ -33,6 +33,7 @@ // (the multi-viewports feature requires SDL features supported from SDL 2.0.5+) #include #include +#define SDL_HAS_WARP_MOUSE_GLOBAL SDL_VERSION_ATLEAST(2,0,4) #define SDL_HAS_CAPTURE_MOUSE SDL_VERSION_ATLEAST(2,0,4) #define SDL_HAS_WINDOW_OPACITY SDL_VERSION_ATLEAST(2,0,5) #define SDL_HAS_ALWAYS_ON_TOP SDL_VERSION_ATLEAST(2,0,5) @@ -131,6 +132,9 @@ bool ImGui_ImplSDL2_Init(SDL_Window* window, void* sdl_gl_context) // Setup back-end capabilities flags ImGuiIO& io = ImGui::GetIO(); io.BackendFlags |= ImGuiBackendFlags_HasMouseCursors; // We can honor GetMouseCursor() values (optional) +#if SDL_HAS_WARP_MOUSE_GLOBAL + io.BackendFlags |= ImGuiBackendFlags_HasSetMousePos; // We can honor io.WantSetMousePos requests (optional, rarely used) +#endif #if SDL_HAS_CAPTURE_MOUSE io.BackendFlags |= ImGuiBackendFlags_PlatformHasViewports; // We can create multi-viewports on the Platform side (optional) #endif @@ -196,10 +200,23 @@ void ImGui_ImplSDL2_Shutdown() static void ImGui_ImplSDL2_UpdateMouse() { ImGuiIO& io = ImGui::GetIO(); + const ImVec2 mouse_pos_backup = io.MousePos; io.MousePos = ImVec2(-FLT_MAX, -FLT_MAX); io.MousePosViewport = 0; io.MouseHoveredViewport = 0; + // Set OS mouse position if requested (rarely used, only when ImGuiConfigFlags_NavEnableSetMousePos is enabled by user) + // (When multi-viewports are enabled, all imgui positions are same as OS positions.) +#if SDL_HAS_WARP_MOUSE_GLOBAL + if (io.WantSetMousePos) + { + if ((io.ConfigFlags & ImGuiConfigFlags_ViewportsEnable) == 0) + SDL_WarpMouseInWindow(g_Window, (int)mouse_pos_backup.x, (int)mouse_pos_backup.y); + else + SDL_WarpMouseGlobal((int)mouse_pos_backup.x, (int)mouse_pos_backup.y); + } +#endif + int mx, my; Uint32 mouse_buttons = SDL_GetMouseState(&mx, &my); io.MouseDown[0] = g_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. diff --git a/examples/imgui_impl_win32.cpp b/examples/imgui_impl_win32.cpp index 097c5dbc..95c92e8e 100644 --- a/examples/imgui_impl_win32.cpp +++ b/examples/imgui_impl_win32.cpp @@ -120,38 +120,53 @@ static bool ImGui_ImplWin32_UpdateMouseCursor() } // This code supports multiple OS Windows mapped into different ImGui viewports, -// So it is a little more complicated than your typical single-viewport binding code (which only needs to set io.MousePos from the WM_MOUSEMOVE handler) -// This is what imgui needs from the back-end to support multiple windows: -// - io.MousePos = mouse position in absolute coordinate (e.g. io.MousePos == ImVec2(0,0) when it is on the upper-left of the primary monitor) -// - io.MousePosViewport = viewport which mouse position is based from (generally the focused/active/capturing viewport) -// - io.MouseHoveredWindow = viewport which mouse is hovering, **regardless of it being the active/focused window**, **regardless of another window holding mouse captured**. [Optional] +// Because of that, it is a little more complicated than your typical single-viewport binding code. +// A) In Single-viewport mode imgui needs: +// - io.MousePos ............... mouse position, in client window coordinates (what you'd get from GetCursorPos+ScreenToClient() or from WM_MOUSEMOVE) +// io.MousePos is (0,0) when the mouse is on the upper-left corner of the application window. +// B) In Multi-viewport mode imgui needs: (when ImGuiConfigFlags_ViewportsEnable is set) +// - io.MousePos ............... mouse position, in OS absolute coordinates (what you'd get from GetCursorPos(), or from WM_MOUSEMOVE+viewport->Pos). +// io.MousePos is (0,0) when the mouse is on the upper-left of the primary monitor. +// - io.MousePosViewport ....... viewport which mouse position is based from (generally the focused/active/capturing viewport) +// - io.MouseHoveredViewport ... [optional] viewport which mouse is hovering, with _very_ specific/strict conditions (Read comments next to io.MouseHoveredViewport. This is _NOT_ easy to provide in many high-level engine because of how we handle the ImGuiViewportFlags_NoInputs flag) // This function overwrite the value of io.MousePos normally updated by the WM_MOUSEMOVE handler. -// We keep the WM_MOUSEMOVE handling code so that WndProc function can be copied as-in in applications which do not need multiple OS windows support. +// We keep the WM_MOUSEMOVE handling code so that WndProc function can be copied as-in in applications which do not need multi-viewport support. static void ImGui_ImplWin32_UpdateMousePos() { ImGuiIO& io = ImGui::GetIO(); + + // Set OS mouse position if requested (rarely used, only when ImGuiConfigFlags_NavEnableSetMousePos is enabled by user) + // (When multi-viewports are enabled, all imgui positions are same as OS positions.) + if (io.WantSetMousePos) + { + POINT pos = { (int)io.MousePos.x, (int)io.MousePos.y }; + if ((io.ConfigFlags & ImGuiConfigFlags_ViewportsEnable) == 0) + ::ClientToScreen(g_hWnd, &pos); + ::SetCursorPos(pos.x, pos.y); + } + io.MousePos = ImVec2(-FLT_MAX, -FLT_MAX); io.MousePosViewport = 0; io.MouseHoveredViewport = 0; + // Set mouse position and viewport + // (Note that ScreenToClient() and adding +viewport->Pos are mutually cancelling each others when we have multi-viewport enabled. In single-viewport mode, viewport->Pos will be zero) POINT pos; if (!::GetCursorPos(&pos)) return; - - // Our back-end can tell which window is under the mouse cursor (not every back-end can), so pass that info to imgui - HWND hovered_hwnd = ::WindowFromPoint(pos); - if (hovered_hwnd) - if (ImGuiViewport* viewport = ImGui::FindViewportByPlatformHandle((void*)hovered_hwnd)) - io.MouseHoveredViewport = viewport->ID; - - // Convert mouse from screen position to window client position - HWND focused_hwnd = ::GetActiveWindow(); - if (focused_hwnd != 0 && ::ScreenToClient(focused_hwnd, &pos)) + if (HWND focused_hwnd = ::GetActiveWindow()) if (ImGuiViewport* viewport = ImGui::FindViewportByPlatformHandle((void*)focused_hwnd)) { - io.MousePos = ImVec2(viewport->Pos.x + (float)pos.x, viewport->Pos.y + (float)pos.y); + POINT client_pos = pos; + ::ScreenToClient(focused_hwnd, &client_pos); + io.MousePos = ImVec2(viewport->Pos.x + (float)client_pos.x, viewport->Pos.y + (float)client_pos.y); io.MousePosViewport = viewport->ID; } + + // Our back-end can tell which window is under the mouse cursor (not every back-end can), so pass that info to imgui + if (HWND hovered_hwnd = ::WindowFromPoint(pos)) + if (ImGuiViewport* viewport = ImGui::FindViewportByPlatformHandle((void*)hovered_hwnd)) + io.MouseHoveredViewport = viewport->ID; } void ImGui_ImplWin32_NewFrame() @@ -176,18 +191,10 @@ void ImGui_ImplWin32_NewFrame() io.KeyShift = (::GetKeyState(VK_SHIFT) & 0x8000) != 0; io.KeyAlt = (::GetKeyState(VK_MENU) & 0x8000) != 0; io.KeySuper = false; - // io.KeysDown : filled by WM_KEYDOWN/WM_KEYUP events - // io.MousePos : filled by WM_MOUSEMOVE events - // io.MouseDown : filled by WM_*BUTTON* events - // io.MouseWheel : filled by WM_MOUSEWHEEL events + // io.KeysDown[], io.MousePos, io.MouseDown[], io.MouseWheel: filled by the WndProc handler below. - // Set OS mouse position if requested (only used when ImGuiConfigFlags_NavEnableSetMousePos is enabled by user) - if (io.WantSetMousePos) - { - POINT pos = { (int)io.MousePos.x, (int)io.MousePos.y }; - ::ClientToScreen(g_hWnd, &pos); - ::SetCursorPos(pos.x, pos.y); - } + // Update OS mouse position + ImGui_ImplWin32_UpdateMousePos(); // Update OS mouse cursor with the cursor requested by imgui ImGuiMouseCursor mouse_cursor = io.MouseDrawCursor ? ImGuiMouseCursor_None : ImGui::GetMouseCursor(); @@ -197,8 +204,6 @@ void ImGui_ImplWin32_NewFrame() ImGui_ImplWin32_UpdateMouseCursor(); } - ImGui_ImplWin32_UpdateMousePos(); - // Start the frame. This call will update the io.WantCaptureMouse, io.WantCaptureKeyboard flag that you can use to dispatch inputs (or not) to your application. ImGui::NewFrame(); }