Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ImGui window coordinate sync issue when out of main viewport (SDL3) #7811

Open
jacobbethany opened this issue Jul 22, 2024 · 0 comments
Open

Comments

@jacobbethany
Copy link

Version/Branch of Dear ImGui:

Newest ImGui docking branch (possible older versions as well)

Back-ends:

imgui_impl_sdl3.cpp + imgui_impl_opengl3cpp

Compiler, OS:

Windows 10 MSVC/G++ w/ msys2/UCRT64

Full config/build information:

Dear ImGui 1.91.0 WIP (19098)
--------------------------------
sizeof(size_t): 8, sizeof(ImDrawIdx): 2, sizeof(ImDrawVert): 20
define: __cplusplus=199711
define: _WIN32
define: _WIN64
define: _MSC_VER=1938
define: _MSVC_LANG=201402
define: IMGUI_HAS_VIEWPORT
define: IMGUI_HAS_DOCK
--------------------------------
io.BackendPlatformName: imgui_impl_sdl3
io.BackendRendererName: imgui_impl_opengl3
io.ConfigFlags: 0x00000483
 NavEnableKeyboard
 NavEnableGamepad
 DockingEnable
 ViewportsEnable
io.ConfigViewportsNoDecoration
io.ConfigInputTextCursorBlink
io.ConfigWindowsResizeFromEdges
io.ConfigMemoryCompactTimer = 60.0
io.BackendFlags: 0x00001C0E
 HasMouseCursors
 HasSetMousePos
 PlatformHasViewports
 HasMouseHoveredViewport
 RendererHasVtxOffset
 RendererHasViewports
--------------------------------
io.Fonts: 1 fonts, Flags: 0x00000000, TexSize: 512,64
io.DisplaySize: 1280.00,720.00
io.DisplayFramebufferScale: 1.00,1.00
--------------------------------
style.WindowPadding: 8.00,8.00
style.WindowBorderSize: 1.00
style.FramePadding: 4.00,3.00
style.FrameRounding: 0.00
style.FrameBorderSize: 0.00
style.ItemSpacing: 8.00,4.00
style.ItemInnerSpacing: 4.00,4.00

Details:

Setup:
-You have the main viewport/OS window.
-You have a second viewport/OS window that was dragged out of the main viewport/OS window.
This bug affects only windows other than the main OS window.

When quickly dragging the second ImGui window across the desktop, the screen coordinates of its native OS window are sometimes different than the position reported by ImGui::GetWindowPos. This can cause the window not to be draggable and/or the controls within the window to require the mouse to be at incorrect coordinates (offset by the delta between the improper window position and the proper window position). This can cause the necessary mouse position to be at screen coordinates which are outside of the window (e.g. the desktop behind it).

When this occurs, the only fix that I've seen work has been to drag the main viewport underneath the affected window to force its viewport to merge (thereby updating its screen coordinates as displayed on the desktop to where ImGui thinks the window should be). Note that merging another window to one that is bugged in this way will cause the same bug with the controls of the merged window. This means that the bug is likely at the viewport level, since ImGui windows' viewports seem to be shared when a merge occurs.

I ran some tests by outputting the window position and cursor position reported by ImGui::GetWindowPos ( ) and io.MousePos.x, io .MousePos.y. I also rebuilt SDL3 with some debugging output to stderr which reported coordinates and captured message types (WM_MOVE/WM_WINDOWPOSCHANGING/WM_WINDOWPOSCHANGED).
Ultimately, it appears that SDL3's reported window position is the same as ImGui's reported cursor position at the top-left of the window as it appears on the desktop, when its in its bugged state, but ImGui's reported window position differs. I'm inclined to treat ImGui's mouse position/SDL3's reported window position as the correct one.

Typically, with WIN32 API, a window which doesn't have a real title bar would be dragged with SendMessageA/W ( hWnd, WM_NCLBUTTONDOWN, (WPARAM) HTCAPTION, (LPARAM) 0 ) to emulate a title bar being clicked upon. This alleviates the burden of synchronizing the window position with the cursor offset from the initial clicked position on a window's title bar by offloading it to the OS.

I see in the code (UpdatePlatformWindows) that SDL_SetWindowPosition is used with each mouse event as the window is being dragged.

if ((viewport->LastPlatformPos.x != viewport->Pos.x || viewport->LastPlatformPos.y != viewport->Pos.y) && !viewport->PlatformRequestMove) {
  g.PlatformIO.Platform_SetWindowPos(viewport, viewport->Pos);
}

It seems like the final mouse position isn't used during a window drag, sometimes. Perhaps the mouse release event is being handled before the SDL_EVENT_WINDOW_MOVED event (which triggers on WM_MOVE, WM_WINDOWPOSCHANGING and WM_WINDOWPOSCHANGED). In any event, it seems like a race condition of some sort.

Anyway, I'd first tried using
SendMessage ( (HWND) viewport ->PlatformHandleRaw, WM_NCLBUTTONDOWN, HTCAPTION, 0 );
to move the window. The "ImGui window" coordinates were updated, but the OS window wasn't actually moving around on the desktop because of some SDL3 shenanigans that I won't get into. I tinkered with SDL3 a bit before deciding it would be more work than it was worth to try to pump extra messages for ImGUI. [Note: removing SDL_FilterEvents from SDL3's SDL_windowevents.c (SDL_SendWindowEvent function) doesn't fix the issue, since only previous messages of the same type/source are removed before adding new ones.]

Ultimately, I just ended up adding to the SDL_EVENT_MOUSE_BUTTON_UP case in imgui_impl_sdl3.cpp's ImGui_ImplSDL3_ProcessEvent function, which triggers an extra ImGui window move request for the current viewport when the left mouse button is released. I haven't been able to replicate the issue since adding this hack.

TL;DR:
The screen/desktop coordinates of the OS window sometimes disagree with ImGui's window coordinates which breaks collision detection between ImGui windows, their components, and the mouse position. SDL3's window coordinates and ImGui's mouse coordinates agree with each other but not with ImGui::GetWindowPos. I added the following code to ensure that the position of the OS window as it is displayed on the screen matches the position ImGui reports for its mouse coordinates on mouse up events.
imgui_impl_sdl3.cpp @ line ~350

        case SDL_EVENT_MOUSE_BUTTON_UP:
        {
            if (ImGuiViewport* viewport = ImGui::FindViewportByPlatformHandle((void*)SDL_GetWindowFromID(event->window.windowID))) {
              viewport->PlatformRequestMove = true;
            }
...

I suspect that this is caused by the order in which the events are acted upon (mouse release vs mouse move vs window position change), but haven't confirmed it. I'm sure there's a more elegant solution than my hack, but I figured that I'd report what I'd found/tried as it will probably be easier for someone with more experience with the internals of ImGui to fix. If you suspect that this issue is more of a problem with SDL3's event propagation, this may need to be forwarded to their team.

Anyway, as before, I hope that this helps. Thanks again for all of your work on this project!

Screenshots/Video:

No response

Minimal, Complete and Verifiable Example code:

// Here's some code anyone can copy and paste to reproduce your issue
ImGui::Begin("Example Bug");
ImGui::Text ( "Cursor position: { %f, %f }", io .MousePos .x, io .MousePos .y );
ImVec2 v2_window_position = ImGui::GetWindowPos (  );
ImVec2 v2_window_dimensions = ImGui::GetWindowSize (  );
ImGui::Text ( "Window position: { %f, %f }", v2_window_position .x, v2_window_position .y, v2_window_dimensions .x, v2_window_dimensions .y );

ImGui::End();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

2 participants