The window sized itself to GetSystemMetrics(SM_CXSCREEN/CYSCREEN) but the process was DPI-unaware, so on a scaled display (e.g. 4K @ 150%) Windows handed us a virtualized resolution and bitmap-stretched the whole window up to native -- softening the mirror, which is the tool's entire point. Declare per-monitor-v2 awareness at startup so GetSystemMetrics/GetDpiForWindow report true pixels. That alone would shrink the fixed-13px ImGui overlay to crisp-but-tiny, so pair it with UI scaling: rebuild the default-font atlas at a DPI-scaled SizePixels (crisp at the target size, unlike FontGlobalScale's bitmap stretch) and ScaleAllSizes() the style. Net: same physical size as before, now sharp. - common/include/coop/dpi.hpp: pure DPI->scale math (uses USER_DEFAULT_SCREEN_DPI and a named kBaseFontPx, not bare 96/13 literals), with a zero fallback and clamping. Unit-tested by tests/dpi_test.cpp. - imgui_layer: apply_dpi() at init from GetDpiForWindow; set_dpi() for runtime changes (rebuild atlas + reset/scale style + invalidate the DX11 font texture). - d3d11_window: latch WM_DPICHANGED (honor the suggested rect), expose take_dpi_change(); main loop polls it and calls imgui.set_dpi() between frames. The DPI math is unit-tested; the actual awareness + font rasterization + live WM_DPICHANGED rescale are verified by hand. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
45 lines
1.9 KiB
C++
45 lines
1.9 KiB
C++
// DPI -> UI scale math for the host overlay, factored out of ImGui/window code so it's unit-testable
|
|
// without a live window. The host is per-monitor-DPI-aware (see wWinMain), so GetSystemMetrics and
|
|
// GetDpiForWindow report true pixels; we scale ImGui's font and style by this factor ourselves to keep
|
|
// the overlay the same physical size -- and crisp -- across display scalings instead of letting Windows
|
|
// bitmap-stretch a virtualized-resolution window.
|
|
#pragma once
|
|
|
|
#include <algorithm>
|
|
|
|
#include <windows.h> // USER_DEFAULT_SCREEN_DPI (== 96, the 100%-scale baseline)
|
|
|
|
namespace coop
|
|
{
|
|
|
|
// ImGui's built-in default font (ProggyClean) rasterizes at this pixel size at 100% scale. Named once
|
|
// here so the DPI math scales from a single owned constant instead of a bare 13 sprinkled around.
|
|
inline constexpr float kBaseFontPx = 13.0f;
|
|
|
|
// Bound the derived scale: never shrink the overlay below 100% (a readability floor), and cap absurd
|
|
// values so a bogus DPI report can't blow the font atlas up to an enormous texture.
|
|
inline constexpr float kMinUiScale = 1.0f;
|
|
inline constexpr float kMaxUiScale = 8.0f;
|
|
|
|
// UI scale factor for a monitor DPI (dots per inch, as GetDpiForWindow / WM_DPICHANGED report it).
|
|
// 96 DPI == 100% == 1.0; 144 DPI (150%) == 1.5. A zero/unknown DPI falls back to the 96 baseline so
|
|
// callers never derive a zero-size font.
|
|
inline float dpi_scale_from(unsigned dpi)
|
|
{
|
|
if (dpi == 0)
|
|
{
|
|
dpi = USER_DEFAULT_SCREEN_DPI;
|
|
}
|
|
const float scale = static_cast<float>(dpi) / static_cast<float>(USER_DEFAULT_SCREEN_DPI);
|
|
return std::clamp(scale, kMinUiScale, kMaxUiScale);
|
|
}
|
|
|
|
// Default-font pixel size for a monitor DPI: the base size scaled so glyphs rasterize crisp at the
|
|
// target size. Pair with ImGui::GetStyle().ScaleAllSizes(dpi_scale_from(dpi)) for spacing/padding.
|
|
inline float scaled_font_px(unsigned dpi)
|
|
{
|
|
return kBaseFontPx * dpi_scale_from(dpi);
|
|
}
|
|
|
|
} // namespace coop
|