chore(window): add [WINLOG] window-dimension tracing for the multi-monitor bug
Always-on stderr tracing (visible in release builds via the Windows dragonx-debug.log redirect, unlike DRAGONX_DEBUG-gated DEBUG_LOGF) to capture exactly how the window size / display-scale / minimum change when dragging between monitors of different DPI. Logs: - every WINDOW_RESIZED: size, actual vs stored scale, dpiPending/settle/retries, and the DPI-vs-user classification (IGNORED/RECORDED); - every DISPLAY_SCALE_CHANGED + the full handleDisplayScaleChange trace (saved-under-old-scale, restore/proportional, min-clamp, display-clamp, the SetWindowMinimumSize+SetWindowSize, and the resulting window state); - each deferred-resize retry; - monitor crossings (WINDOW_MOVED, throttled to display changes); - startup and shutdown size. Low volume — only fires on window events. Temporary diagnostic; remove once the multi-monitor sizing bug is fixed. Grep the log for "[WINLOG]". Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
70
src/main.cpp
70
src/main.cpp
@@ -71,6 +71,7 @@ extern "C" HRESULT __stdcall SHGetPropertyStoreForWindow(HWND hwnd, REFIID riid,
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
#include <cstdio>
|
#include <cstdio>
|
||||||
|
#include <cstdarg>
|
||||||
#include <cstdlib>
|
#include <cstdlib>
|
||||||
#include <cmath>
|
#include <cmath>
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
@@ -531,6 +532,32 @@ static void drawWindowBackdrop(dragonx::App& app, ImDrawList* bgDL, ImVec2 p0, I
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ── Window-dimension diagnostic tracer ──────────────────────────────────────────────────────
|
||||||
|
// Always-on (writes to stderr → dragonx-debug.log on Windows) so it's visible in release builds,
|
||||||
|
// unlike DRAGONX_DEBUG-gated DEBUG_LOGF. Grep the log for "[WINLOG]" to trace how the window size
|
||||||
|
// changes when dragging between monitors of different DPI. Low volume — only fires on window
|
||||||
|
// resize / move / DPI-scale events. TODO: remove once the multi-monitor sizing bug is resolved.
|
||||||
|
static void winlog(const char* fmt, ...)
|
||||||
|
{
|
||||||
|
char buf[1024];
|
||||||
|
va_list a; va_start(a, fmt); vsnprintf(buf, sizeof(buf), fmt, a); va_end(a);
|
||||||
|
fprintf(stderr, "[WINLOG] %s\n", buf);
|
||||||
|
fflush(stderr);
|
||||||
|
}
|
||||||
|
// Dump the full current window state with a short tag.
|
||||||
|
static void winlogState(SDL_Window* w, const char* tag)
|
||||||
|
{
|
||||||
|
int sw = 0, sh = 0, pw = 0, ph = 0, mw = 0, mh = 0;
|
||||||
|
SDL_GetWindowSize(w, &sw, &sh);
|
||||||
|
SDL_GetWindowSizeInPixels(w, &pw, &ph);
|
||||||
|
SDL_GetWindowMinimumSize(w, &mw, &mh);
|
||||||
|
float ds = SDL_GetWindowDisplayScale(w);
|
||||||
|
float st = dragonx::ui::material::Typography::instance().getDpiScale();
|
||||||
|
SDL_DisplayID did = SDL_GetDisplayForWindow(w);
|
||||||
|
winlog("%-20s size=%dx%d px=%dx%d min=%dx%d dispScale=%.3f storedScale=%.3f display=%u",
|
||||||
|
tag, sw, sh, pw, ph, mw, mh, ds, st, (unsigned)did);
|
||||||
|
}
|
||||||
|
|
||||||
// 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
|
||||||
@@ -548,6 +575,9 @@ static void handleDisplayScaleChange(SDL_Window* window, float newScale,
|
|||||||
float ratio = newScale / oldScale;
|
float ratio = newScale / oldScale;
|
||||||
DEBUG_LOGF("Display scale changed: %.2f -> %.2f (ratio %.2f)\n",
|
DEBUG_LOGF("Display scale changed: %.2f -> %.2f (ratio %.2f)\n",
|
||||||
oldScale, newScale, ratio);
|
oldScale, newScale, ratio);
|
||||||
|
winlog("======== DPI CHANGE storedScale %.3f -> newScale %.3f (ratio %.3f) ========",
|
||||||
|
oldScale, newScale, ratio);
|
||||||
|
winlogState(window, " entry");
|
||||||
ImGuiIO& io = ImGui::GetIO();
|
ImGuiIO& io = ImGui::GetIO();
|
||||||
|
|
||||||
#ifdef DRAGONX_USE_DX11
|
#ifdef DRAGONX_USE_DX11
|
||||||
@@ -563,6 +593,7 @@ static void handleDisplayScaleChange(SDL_Window* window, float newScale,
|
|||||||
savedSizeForScale[oldPct] = {lastKnownW, lastKnownH};
|
savedSizeForScale[oldPct] = {lastKnownW, lastKnownH};
|
||||||
DEBUG_LOGF(" Saved %dx%d for scale %d%%\n",
|
DEBUG_LOGF(" Saved %dx%d for scale %d%%\n",
|
||||||
lastKnownW, lastKnownH, oldPct);
|
lastKnownW, lastKnownH, oldPct);
|
||||||
|
winlog(" saved lastKnown %dx%d under old scale %d%%", lastKnownW, lastKnownH, oldPct);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Compute new physical size: if we've been to this
|
// Compute new physical size: if we've been to this
|
||||||
@@ -574,12 +605,15 @@ static void handleDisplayScaleChange(SDL_Window* window, float newScale,
|
|||||||
if (it != savedSizeForScale.end()) {
|
if (it != savedSizeForScale.end()) {
|
||||||
newW = it->second.first;
|
newW = it->second.first;
|
||||||
newH = it->second.second;
|
newH = it->second.second;
|
||||||
|
winlog(" restore saved %dx%d for scale %d%%", newW, newH, newPct);
|
||||||
} else {
|
} else {
|
||||||
// Use lastKnownW/H (pre-auto-resize) instead of
|
// Use lastKnownW/H (pre-auto-resize) instead of
|
||||||
// SDL_GetWindowSize() which already reflects the
|
// SDL_GetWindowSize() which already reflects the
|
||||||
// WM_DPICHANGED auto-resize.
|
// WM_DPICHANGED auto-resize.
|
||||||
newW = (int)lroundf((float)lastKnownW * newScale / oldScale);
|
newW = (int)lroundf((float)lastKnownW * newScale / oldScale);
|
||||||
newH = (int)lroundf((float)lastKnownH * newScale / oldScale);
|
newH = (int)lroundf((float)lastKnownH * newScale / oldScale);
|
||||||
|
winlog(" proportional %dx%d = lastKnown %dx%d * %.3f/%.3f",
|
||||||
|
newW, newH, lastKnownW, lastKnownH, newScale, oldScale);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Never restore/scale below the new scale's minimum — guarantees the window lands exactly
|
// Never restore/scale below the new scale's minimum — guarantees the window lands exactly
|
||||||
@@ -587,6 +621,7 @@ static void handleDisplayScaleChange(SDL_Window* window, float newScale,
|
|||||||
const int minW = (int)(1024 * newScale), minH = (int)(720 * newScale);
|
const int minW = (int)(1024 * newScale), minH = (int)(720 * newScale);
|
||||||
newW = std::max(newW, minW);
|
newW = std::max(newW, minW);
|
||||||
newH = std::max(newH, minH);
|
newH = std::max(newH, minH);
|
||||||
|
winlog(" after min-clamp (min %dx%d): %dx%d", minW, minH, newW, newH);
|
||||||
|
|
||||||
// Clamp to the target display's work area so the
|
// Clamp to the target display's work area so the
|
||||||
// window doesn't overflow a smaller/higher-density monitor.
|
// window doesn't overflow a smaller/higher-density monitor.
|
||||||
@@ -596,6 +631,7 @@ static void handleDisplayScaleChange(SDL_Window* window, float newScale,
|
|||||||
if (SDL_GetDisplayUsableBounds(did, &usable)) {
|
if (SDL_GetDisplayUsableBounds(did, &usable)) {
|
||||||
newW = std::min(newW, usable.w);
|
newW = std::min(newW, usable.w);
|
||||||
newH = std::min(newH, usable.h);
|
newH = std::min(newH, usable.h);
|
||||||
|
winlog(" after display-clamp (usable %dx%d): %dx%d", usable.w, usable.h, newW, newH);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -619,6 +655,9 @@ static void handleDisplayScaleChange(SDL_Window* window, float newScale,
|
|||||||
dpiSettleFrames = 40; // ignore resize-recording until the transition settles
|
dpiSettleFrames = 40; // ignore resize-recording until the transition settles
|
||||||
DEBUG_LOGF(" Window resized: %dx%d (scale %.2f, pct %d)\n",
|
DEBUG_LOGF(" Window resized: %dx%d (scale %.2f, pct %d)\n",
|
||||||
newW, newH, newScale, newPct);
|
newW, newH, newScale, newPct);
|
||||||
|
winlog(" SetWindowMinimumSize(%dx%d) then SetWindowSize(%dx%d); armed retries=30 settle=40",
|
||||||
|
minW, minH, newW, newH);
|
||||||
|
winlogState(window, " after set");
|
||||||
|
|
||||||
// 2) Rebuild font atlas at the new DPI scale
|
// 2) Rebuild font atlas at the new DPI scale
|
||||||
typo.reload(io, newScale);
|
typo.reload(io, newScale);
|
||||||
@@ -1110,6 +1149,8 @@ int main(int argc, char* argv[])
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
winlog("STARTUP savedSize=%dx%d currentDpiScale=%.3f", savedWinW, savedWinH, currentDpiScale);
|
||||||
|
winlogState(window, " startup");
|
||||||
|
|
||||||
// Create application instance
|
// Create application instance
|
||||||
dragonx::App app;
|
dragonx::App app;
|
||||||
@@ -1293,6 +1334,7 @@ int main(int argc, char* argv[])
|
|||||||
// Frames to keep ignoring resize-recording after a DPI transition (settle window) so transient
|
// 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.
|
// WINDOW_RESIZED events during the transition can't corrupt savedSizeForScale / lastKnownW/H.
|
||||||
int dpiSettleFrames = 0;
|
int dpiSettleFrames = 0;
|
||||||
|
SDL_DisplayID lastLoggedDisplay = 0; // [WINLOG] throttle: log MOVED only when the display changes
|
||||||
{
|
{
|
||||||
float s = dragonx::ui::material::Typography::instance().getDpiScale();
|
float s = dragonx::ui::material::Typography::instance().getDpiScale();
|
||||||
int w = 0, h = 0;
|
int w = 0, h = 0;
|
||||||
@@ -1315,8 +1357,11 @@ int main(int argc, char* argv[])
|
|||||||
SDL_SetWindowSize(window, dpiTargetW, dpiTargetH);
|
SDL_SetWindowSize(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)",
|
||||||
|
dpiResizeRetries, curW, curH, dpiTargetW, dpiTargetH);
|
||||||
} else {
|
} else {
|
||||||
// Size matches — done
|
// Size matches — done
|
||||||
|
winlog("RETRY done: reached target %dx%d", dpiTargetW, dpiTargetH);
|
||||||
dpiResizeRetries = 0;
|
dpiResizeRetries = 0;
|
||||||
}
|
}
|
||||||
--dpiResizeRetries;
|
--dpiResizeRetries;
|
||||||
@@ -1357,6 +1402,10 @@ int main(int argc, char* argv[])
|
|||||||
#endif
|
#endif
|
||||||
bool isDpiResize = dpiSettleFrames > 0 || dpiResizePending ||
|
bool isDpiResize = dpiSettleFrames > 0 || dpiResizePending ||
|
||||||
std::abs(actualScale - storedScale) > 0.01f;
|
std::abs(actualScale - storedScale) > 0.01f;
|
||||||
|
winlog("RESIZED(wait) evt=%dx%d actualScale=%.3f storedScale=%.3f dpiPending=%d settle=%d retries=%d -> %s",
|
||||||
|
waitEvent.window.data1, waitEvent.window.data2, actualScale, storedScale,
|
||||||
|
(int)dpiResizePending, dpiSettleFrames, dpiResizeRetries,
|
||||||
|
isDpiResize ? "IGNORED(dpi)" : "RECORDED(user)");
|
||||||
if (dpiResizePending) dpiResizePending = false;
|
if (dpiResizePending) dpiResizePending = false;
|
||||||
if (!isDpiResize) {
|
if (!isDpiResize) {
|
||||||
int pct = (int)lroundf(storedScale * 100.0f);
|
int pct = (int)lroundf(storedScale * 100.0f);
|
||||||
@@ -1378,6 +1427,7 @@ int main(int argc, char* argv[])
|
|||||||
#ifdef __APPLE__
|
#ifdef __APPLE__
|
||||||
newScale = 1.0f; // macOS handles Retina via DisplayFramebufferScale
|
newScale = 1.0f; // macOS handles Retina via DisplayFramebufferScale
|
||||||
#endif
|
#endif
|
||||||
|
winlog("SCALE_CHANGED(wait) reported newScale=%.3f", newScale);
|
||||||
handleDisplayScaleChange(window, newScale, savedSizeForScale,
|
handleDisplayScaleChange(window, newScale, savedSizeForScale,
|
||||||
lastKnownW, lastKnownH, dpiTargetW, dpiTargetH,
|
lastKnownW, lastKnownH, dpiTargetW, dpiTargetH,
|
||||||
dpiResizeRetries, dpiResizePending, dpiSettleFrames);
|
dpiResizeRetries, dpiResizePending, dpiSettleFrames);
|
||||||
@@ -1437,10 +1487,22 @@ int main(int argc, char* argv[])
|
|||||||
if (event.type == SDL_EVENT_QUIT) {
|
if (event.type == SDL_EVENT_QUIT) {
|
||||||
app.beginShutdown();
|
app.beginShutdown();
|
||||||
}
|
}
|
||||||
if (event.type == SDL_EVENT_WINDOW_CLOSE_REQUESTED &&
|
if (event.type == SDL_EVENT_WINDOW_CLOSE_REQUESTED &&
|
||||||
event.window.windowID == SDL_GetWindowID(window)) {
|
event.window.windowID == SDL_GetWindowID(window)) {
|
||||||
app.beginShutdown();
|
app.beginShutdown();
|
||||||
}
|
}
|
||||||
|
// [WINLOG] Log a window MOVE only when it crosses to a different display (avoids drag spam).
|
||||||
|
if (event.type == SDL_EVENT_WINDOW_MOVED &&
|
||||||
|
event.window.windowID == SDL_GetWindowID(window)) {
|
||||||
|
SDL_DisplayID d = SDL_GetDisplayForWindow(window);
|
||||||
|
if (d != lastLoggedDisplay) {
|
||||||
|
lastLoggedDisplay = d;
|
||||||
|
winlog("MOVED to display %u pos=%d,%d dispScale=%.3f",
|
||||||
|
(unsigned)d, event.window.data1, event.window.data2,
|
||||||
|
SDL_GetWindowDisplayScale(window));
|
||||||
|
winlogState(window, " on-cross");
|
||||||
|
}
|
||||||
|
}
|
||||||
#ifdef DRAGONX_USE_DX11
|
#ifdef DRAGONX_USE_DX11
|
||||||
// Handle window resize for DX11 swap chain
|
// Handle window resize for DX11 swap chain
|
||||||
if (event.type == SDL_EVENT_WINDOW_RESIZED &&
|
if (event.type == SDL_EVENT_WINDOW_RESIZED &&
|
||||||
@@ -1460,6 +1522,10 @@ int main(int argc, char* argv[])
|
|||||||
#endif
|
#endif
|
||||||
bool isDpiResize = dpiSettleFrames > 0 || dpiResizePending ||
|
bool isDpiResize = dpiSettleFrames > 0 || dpiResizePending ||
|
||||||
std::abs(actualScale - storedScale) > 0.01f;
|
std::abs(actualScale - storedScale) > 0.01f;
|
||||||
|
winlog("RESIZED(poll) evt=%dx%d actualScale=%.3f storedScale=%.3f dpiPending=%d settle=%d retries=%d fsPending=%d -> %s",
|
||||||
|
event.window.data1, event.window.data2, actualScale, storedScale,
|
||||||
|
(int)dpiResizePending, dpiSettleFrames, dpiResizeRetries, (int)fontScaleResizePending,
|
||||||
|
isDpiResize ? "IGNORED(dpi)" : "RECORDED(user)");
|
||||||
if (dpiResizePending) dpiResizePending = false;
|
if (dpiResizePending) dpiResizePending = false;
|
||||||
bool isFSResize = fontScaleResizePending;
|
bool isFSResize = fontScaleResizePending;
|
||||||
if (fontScaleResizePending) fontScaleResizePending = false;
|
if (fontScaleResizePending) fontScaleResizePending = false;
|
||||||
@@ -1491,6 +1557,7 @@ int main(int argc, char* argv[])
|
|||||||
#ifdef __APPLE__
|
#ifdef __APPLE__
|
||||||
newScale = 1.0f; // macOS handles Retina via DisplayFramebufferScale
|
newScale = 1.0f; // macOS handles Retina via DisplayFramebufferScale
|
||||||
#endif
|
#endif
|
||||||
|
winlog("SCALE_CHANGED(poll) reported newScale=%.3f", newScale);
|
||||||
handleDisplayScaleChange(window, newScale, savedSizeForScale,
|
handleDisplayScaleChange(window, newScale, savedSizeForScale,
|
||||||
lastKnownW, lastKnownH, dpiTargetW, dpiTargetH,
|
lastKnownW, lastKnownH, dpiTargetW, dpiTargetH,
|
||||||
dpiResizeRetries, dpiResizePending, dpiSettleFrames);
|
dpiResizeRetries, dpiResizePending, dpiSettleFrames);
|
||||||
@@ -1879,6 +1946,7 @@ int main(int argc, char* argv[])
|
|||||||
float s = dragonx::ui::material::Typography::instance().getDpiScale();
|
float s = dragonx::ui::material::Typography::instance().getDpiScale();
|
||||||
int logW = (int)lroundf((float)curW / s);
|
int logW = (int)lroundf((float)curW / s);
|
||||||
int logH = (int)lroundf((float)curH / s);
|
int logH = (int)lroundf((float)curH / s);
|
||||||
|
winlog("SHUTDOWN save: size=%dx%d storedScale=%.3f -> logical=%dx%d", curW, curH, s, logW, logH);
|
||||||
if (logW >= 1024 && logH >= 720) {
|
if (logW >= 1024 && logH >= 720) {
|
||||||
app.settings()->setWindowSize(logW, logH);
|
app.settings()->setWindowSize(logW, logH);
|
||||||
app.settings()->save();
|
app.settings()->save();
|
||||||
|
|||||||
Reference in New Issue
Block a user