From eb897d0f1b7a24e132438750b3fa8b65442da390 Mon Sep 17 00:00:00 2001 From: bicarus <202771338+bicarus-dev@users.noreply.github.com> Date: Wed, 17 Jun 2026 20:12:56 -0700 Subject: [PATCH] overlay: fix cursor visibility (#766) After #739 there was a regression where the cursor visibility became inconsistent if the FPS display was shown and then another overlay window was toggled. Fix the imgui cursor toggle logic to only check for the overlay layer visibility. --- src/spice2x/overlay/imgui/impl_spice.cpp | 64 ++++++++++++++++++------ 1 file changed, 48 insertions(+), 16 deletions(-) diff --git a/src/spice2x/overlay/imgui/impl_spice.cpp b/src/spice2x/overlay/imgui/impl_spice.cpp index 9a6c9a2..844d16c 100644 --- a/src/spice2x/overlay/imgui/impl_spice.cpp +++ b/src/spice2x/overlay/imgui/impl_spice.cpp @@ -352,6 +352,52 @@ static void ImGui_ImplSpice_UpdateMousePos() { } } +// Decide whether ImGui should draw its software cursor this frame. Kept out of +// ImGui_ImplSpice_NewFrame so the visibility rules live in one place. +// +// Only matters when ImGui owns cursor drawing (g_MouseCursorAutoHide, i.e. the +// game hid the OS cursor); otherwise the OS draws the cursor and +// io.MouseDrawCursor stays false throughout. In that mode: +// - overlay hidden -> never draw the cursor (input belongs to the game) +// - overlay shown -> visible immediately, auto-hide after 2s idle, reappear +// on any mouse movement / wheel / button +static void ImGui_ImplSpice_UpdateCursorVisibility( + bool overlay_visible, + const ImVec2 &old_mouse_pos, + const ImVec2 &new_mouse_pos, + long wheel_diff) +{ + if (!g_MouseCursorAutoHide) { + return; + } + + auto &io = ImGui::GetIO(); + static bool overlay_visible_old = false; + + const bool mouse_activity = + old_mouse_pos.x != new_mouse_pos.x || + old_mouse_pos.y != new_mouse_pos.y || + wheel_diff != 0 || + g_MouseDown[ImGuiMouseButton_Left] || + g_MouseDown[ImGuiMouseButton_Right] || + g_MouseDown[ImGuiMouseButton_Middle]; + + if (!overlay_visible) { + // overlay hidden: the game owns the cursor, don't draw ImGui's + io.MouseDrawCursor = false; + } else if (!overlay_visible_old || mouse_activity) { + // overlay just opened, or the mouse moved: show the cursor and + // (re)start the idle timer + g_LastMouseMovement = get_performance_milliseconds(); + io.MouseDrawCursor = true; + } else if ((get_performance_milliseconds() - g_LastMouseMovement) > 2000) { + // mouse idle for more than 2 seconds while the overlay is open: hide it + io.MouseDrawCursor = false; + } + + overlay_visible_old = overlay_visible; +} + void ImGui_ImplSpice_NewFrame() { // check if font is built @@ -527,22 +573,8 @@ void ImGui_ImplSpice_NewFrame() { } } - // automatically hide cursor - if (g_MouseCursorAutoHide) { - if (old_mouse_pos.x != new_mouse_pos.x || - old_mouse_pos.y != new_mouse_pos.y || - wheel_diff != 0 || - g_MouseDown[ImGuiMouseButton_Left] || g_MouseDown[ImGuiMouseButton_Right] || g_MouseDown[ImGuiMouseButton_Middle]) { - - // mouse moved, update time and show the cursor - g_LastMouseMovement = get_performance_milliseconds(); - io.MouseDrawCursor = true; - - } else if ((get_performance_milliseconds() - g_LastMouseMovement) > 2000) { - // mouse idle for more than 2 seconds, hide the cursor - io.MouseDrawCursor = false; - } - } + // mouse cursor: visibility (overlay visibility + auto-hide) + ImGui_ImplSpice_UpdateCursorVisibility(overlay_visible, old_mouse_pos, new_mouse_pos, wheel_diff); if (cfg::CONFIGURATOR_STANDALONE) { // if cursor is inside the client area, always set the OS cursor to what ImGui wants