From 53da1605465c7c1e1af0efe2a6d933a875719969 Mon Sep 17 00:00:00 2001 From: DanS Date: Tue, 13 Oct 2026 18:16:50 -0500 Subject: [PATCH] fix(window): defer caption-button window ops out of the ImGui frame The custom minimize / maximize / restore buttons are drawn from inside app.render(), while an ImGui frame is open. Calling SDL to change the window state there pushes resize events synchronously, which re-enters the resize watcher and can nest a new frame inside the open one. - A click now records the requested op instead of acting on it, and the window state change is applied once the frame has closed, after the pacing block so the follow-up redraw is not dropped. Windows-only; it rides the borderless caption controls. Co-Authored-By: Claude Opus 4.8 --- src/main.cpp | 24 +++++++++++++++++++----- 1 file changed, 19 insertions(+), 5 deletions(-) diff --git a/src/main.cpp b/src/main.cpp index 0278d10..3996182 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -334,6 +334,11 @@ static LRESULT CALLBACK BorderlessWndProc(HWND hwnd, UINT msg, return CallWindowProcW(g_sdlWndProc, hwnd, msg, wParam, lParam); } +// Caption-button window ops are deferred out of the open ImGui frame and run +// after the frame closes (SDL pushes resize events synchronously). (C146) +enum class CaptionOp { None, Minimize, Maximize, Restore }; +static CaptionOp g_pendingCaptionOp = CaptionOp::None; + // Draw custom window-control buttons (minimize / maximize-restore / close) // on the foreground draw list, and handle title-bar dragging. static void renderBorderlessControls(SDL_Window* window, HWND /*hwnd*/, @@ -380,7 +385,7 @@ static void renderBorderlessControls(SDL_Window* window, HWND /*hwnd*/, float cy = oy + btnH * 0.5f, lx = minX + btnW * 0.5f - iconHalf; fg->AddLine(ImVec2(lx, cy), ImVec2(lx + iconHalf * 2.0f, cy), wcIcon, lineThk); - if (hov && click) SDL_MinimizeWindow(window); + if (hov && click) g_pendingCaptionOp = CaptionOp::Minimize; } // --- Maximize / Restore button --- @@ -402,10 +407,7 @@ static void renderBorderlessControls(SDL_Window* window, HWND /*hwnd*/, ImVec2(cx + iconHalf, cy + iconHalf - dp), wcIcon, 0, 0, lineThk); } - if (hov && click) { - if (maximized) SDL_RestoreWindow(window); - else SDL_MaximizeWindow(window); - } + if (hov && click) g_pendingCaptionOp = maximized ? CaptionOp::Restore : CaptionOp::Maximize; } // --- Close button --- @@ -2374,6 +2376,18 @@ int main(int argc, char* argv[]) nextAmbientFrameMs = lastFrameStartNs / 1000000 + interval; } } +#ifdef DRAGONX_USE_DX11 + if (g_pendingCaptionOp != CaptionOp::None) { + switch (g_pendingCaptionOp) { + case CaptionOp::Minimize: SDL_MinimizeWindow(window); break; + case CaptionOp::Maximize: SDL_MaximizeWindow(window); break; + case CaptionOp::Restore: SDL_RestoreWindow(window); break; + default: break; + } + g_pendingCaptionOp = CaptionOp::None; + needsRedraw = true; // after the pacing block, so it is not overwritten + } +#endif } // Remove resize event watcher