Files
CoopAllTheThings/common/include/coop/dpi.hpp
BlackMark d73b43ad0d Make the host per-monitor DPI aware and scale the ImGui overlay
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>
2026-07-01 23:23:41 +02:00

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