diff --git a/src/main.cpp b/src/main.cpp index b9c2082..38481cc 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -539,7 +539,8 @@ static void handleDisplayScaleChange(SDL_Window* window, float newScale, std::map>& savedSizeForScale, int& lastKnownW, int& lastKnownH, int& dpiTargetW, int& dpiTargetH, - int& dpiResizeRetries, bool& dpiResizePending) + int& dpiResizeRetries, bool& dpiResizePending, + int& dpiSettleFrames) { auto& typo = dragonx::ui::material::Typography::instance(); float oldScale = typo.getDpiScale(); @@ -581,6 +582,12 @@ static void handleDisplayScaleChange(SDL_Window* window, float newScale, newH = (int)lroundf((float)lastKnownH * newScale / oldScale); } + // Never restore/scale below the new scale's minimum — guarantees the window lands exactly + // on the min in both directions even if the per-scale map was ever seeded oddly. + const int minW = (int)(1024 * newScale), minH = (int)(720 * newScale); + newW = std::max(newW, minW); + newH = std::max(newH, minH); + // Clamp to the target display's work area so the // window doesn't overflow a smaller/higher-density monitor. SDL_DisplayID did = SDL_GetDisplayForWindow(window); @@ -592,18 +599,24 @@ static void handleDisplayScaleChange(SDL_Window* window, float newScale, } } + // Set the new minimum BEFORE resizing. SDL_SetWindowSize clamps the request UP to the + // current minimum, so when moving to a LOWER scale the shrink (e.g. 1080 -> 720) issued + // while the min is still the old 1080 would be clamped back to 1080 and stick there + // (SDL_SetWindowMinimumSize only ever grows a window, never shrinks it). Min-then-size + // makes the shrink land on the new min immediately, without relying on the deferred retry. + SDL_SetWindowMinimumSize(window, minW, minH); dpiResizePending = true; SDL_SetWindowSize(window, newW, newH); lastKnownW = newW; lastKnownH = newH; - // Arm deferred retry — the SetWindowSize above may - // not stick if the user is mid-drag (modal loop). + // Arm deferred retry (SetWindowSize may not stick mid-drag) and suppress resize-recording + // for the whole settle burst: a transient WINDOW_RESIZED (ours, the WM_DPICHANGED settle, + // or a retry frame) that arrives after dpiResizePending was consumed and with scales already + // matching would otherwise be misclassified as a user resize and corrupt savedSizeForScale. dpiTargetW = newW; dpiTargetH = newH; dpiResizeRetries = 30; // retry for ~30 frames - SDL_SetWindowMinimumSize(window, - (int)(1024 * newScale), - (int)(720 * newScale)); + dpiSettleFrames = 40; // ignore resize-recording until the transition settles DEBUG_LOGF(" Window resized: %dx%d (scale %.2f, pct %d)\n", newW, newH, newScale, newPct); @@ -1277,6 +1290,9 @@ int main(int argc, char* argv[]) // overrides SetWindowPos). We retry for several frames. int dpiTargetW = 0, dpiTargetH = 0; int dpiResizeRetries = 0; + // Frames to keep ignoring resize-recording after a DPI transition (settle window) so transient + // WINDOW_RESIZED events during the transition can't corrupt savedSizeForScale / lastKnownW/H. + int dpiSettleFrames = 0; { float s = dragonx::ui::material::Typography::instance().getDpiScale(); int w = 0, h = 0; @@ -1306,6 +1322,9 @@ int main(int argc, char* argv[]) --dpiResizeRetries; needsRedraw = true; } + // Burn down the post-DPI-transition settle window (see handleDisplayScaleChange). The retry + // above forces redraws while active, so this drains promptly even if the app is otherwise idle. + if (dpiSettleFrames > 0) --dpiSettleFrames; // P6: Idle rendering — sleep if nothing needs redrawing if (!needsRedraw) { @@ -1336,7 +1355,7 @@ int main(int argc, char* argv[]) #ifdef __APPLE__ actualScale = storedScale; // macOS Retina: scales always match (both 1.0) #endif - bool isDpiResize = dpiResizePending || + bool isDpiResize = dpiSettleFrames > 0 || dpiResizePending || std::abs(actualScale - storedScale) > 0.01f; if (dpiResizePending) dpiResizePending = false; if (!isDpiResize) { @@ -1361,7 +1380,7 @@ int main(int argc, char* argv[]) #endif handleDisplayScaleChange(window, newScale, savedSizeForScale, lastKnownW, lastKnownH, dpiTargetW, dpiTargetH, - dpiResizeRetries, dpiResizePending); + dpiResizeRetries, dpiResizePending, dpiSettleFrames); } needsRedraw = true; // Got an event — redraw } @@ -1439,7 +1458,7 @@ int main(int argc, char* argv[]) #ifdef __APPLE__ actualScale = storedScale; // macOS Retina: scales always match (both 1.0) #endif - bool isDpiResize = dpiResizePending || + bool isDpiResize = dpiSettleFrames > 0 || dpiResizePending || std::abs(actualScale - storedScale) > 0.01f; if (dpiResizePending) dpiResizePending = false; bool isFSResize = fontScaleResizePending; @@ -1474,7 +1493,7 @@ int main(int argc, char* argv[]) #endif handleDisplayScaleChange(window, newScale, savedSizeForScale, lastKnownW, lastKnownH, dpiTargetW, dpiTargetH, - dpiResizeRetries, dpiResizePending); + dpiResizeRetries, dpiResizePending, dpiSettleFrames); } }