fix(window): resize the borderless HWND directly so DPI-move sizes are exact

The hardware [WINLOG] trace showed the DPI-transition target computed correctly
(min height 720) but the window stuck ~5% larger (759), with every deferred retry
failing to shrink it. Root cause: the DX11 window is a borderless native HWND
(WS_CAPTION/WS_THICKFRAME, for native move/resize) wrapped by SDL. SDL_SetWindowSize
converts the requested CLIENT size to a window rect via AdjustWindowRectEx (adding
the caption+border frame), but our WM_NCCALCSIZE handler folds that whole frame
back into the client area — so every SDL resize inflates the window by ~one frame
(~39px height, ~16px width) and the 720 minimum becomes an unreachable 759. That,
not the earlier min-ordering/settle logic, is why the height wouldn't stay at min.

resizeWindowExact() resizes the native HWND directly via SetWindowPos on the DX11
path (window rect == client for a borderless window → exact requested size); other
platforms keep SDL_SetWindowSize. Used for the DPI-transition resize and the
deferred retry, so the window now lands on the true minimum in both directions.

Keeps the [WINLOG] native-rect + min-track tracing so the next capture confirms the
fix (or surfaces any remaining gap).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-07-10 01:08:58 -05:00
parent 010465f835
commit a80feb708e

View File

@@ -578,6 +578,25 @@ static void winlogState(SDL_Window* w, const char* tag)
#endif #endif
} }
// Resize the window to an EXACT client size. On the DX11 borderless native HWND, SDL_SetWindowSize
// inflates the result by a phantom caption+border frame: SDL runs AdjustWindowRectEx to turn the
// requested client size into a window rect (adding the WS_CAPTION/WS_THICKFRAME frame), but our
// WM_NCCALCSIZE handler folds that entire window rect back into the client area — so the client ends
// up ~frame bigger than asked (e.g. a 720 min becomes 759, unreachable). Resize the HWND directly:
// for a borderless window the window rect equals the client, so SetWindowPos gives the exact size.
static void resizeWindowExact(SDL_Window* window, int w, int h)
{
#if defined(_WIN32) && defined(DRAGONX_USE_DX11)
HWND hwnd = (HWND)SDL_GetPointerProperty(SDL_GetWindowProperties(window),
SDL_PROP_WINDOW_WIN32_HWND_POINTER, nullptr);
if (hwnd) {
SetWindowPos(hwnd, nullptr, 0, 0, w, h, SWP_NOMOVE | SWP_NOZORDER | SWP_NOACTIVATE);
return;
}
#endif
SDL_SetWindowSize(window, w, h);
}
// Handle SDL_EVENT_WINDOW_DISPLAY_SCALE_CHANGED: save the old-scale size, // Handle SDL_EVENT_WINDOW_DISPLAY_SCALE_CHANGED: save the old-scale size,
// compute + clamp the new physical size, arm the deferred resize retry, // compute + clamp the new physical size, arm the deferred resize retry,
// and rebuild fonts / reset ImGui style at the new DPI scale. newScale // and rebuild fonts / reset ImGui style at the new DPI scale. newScale
@@ -662,7 +681,7 @@ static void handleDisplayScaleChange(SDL_Window* window, float newScale,
// makes the shrink land on the new min immediately, without relying on the deferred retry. // makes the shrink land on the new min immediately, without relying on the deferred retry.
SDL_SetWindowMinimumSize(window, minW, minH); SDL_SetWindowMinimumSize(window, minW, minH);
dpiResizePending = true; dpiResizePending = true;
SDL_SetWindowSize(window, newW, newH); resizeWindowExact(window, newW, newH);
lastKnownW = newW; lastKnownW = newW;
lastKnownH = newH; lastKnownH = newH;
// Arm deferred retry (SetWindowSize may not stick mid-drag) and suppress resize-recording // Arm deferred retry (SetWindowSize may not stick mid-drag) and suppress resize-recording
@@ -1374,7 +1393,7 @@ int main(int argc, char* argv[])
SDL_GetWindowSize(window, &curW, &curH); SDL_GetWindowSize(window, &curW, &curH);
if (curW != dpiTargetW || curH != dpiTargetH) { if (curW != dpiTargetW || curH != dpiTargetH) {
dpiResizePending = true; dpiResizePending = true;
SDL_SetWindowSize(window, dpiTargetW, dpiTargetH); resizeWindowExact(window, dpiTargetW, dpiTargetH);
DEBUG_LOGF(" Deferred resize retry %d: %dx%d -> %dx%d\n", DEBUG_LOGF(" Deferred resize retry %d: %dx%d -> %dx%d\n",
dpiResizeRetries, curW, curH, dpiTargetW, dpiTargetH); dpiResizeRetries, curW, curH, dpiTargetW, dpiTargetH);
winlog("RETRY %d: cur %dx%d != target %dx%d -> SetWindowSize(target)", winlog("RETRY %d: cur %dx%d != target %dx%d -> SetWindowSize(target)",