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>
This commit is contained in:
@@ -105,6 +105,11 @@ add_executable(tool_paths_test tool_paths_test.cpp)
|
||||
target_link_libraries(tool_paths_test PRIVATE coop_common)
|
||||
add_test(NAME tool_paths_test COMMAND tool_paths_test)
|
||||
|
||||
# Unit test for the DPI -> UI scale math (baseline/known scalings, zero fallback, clamping, font px).
|
||||
add_executable(dpi_test dpi_test.cpp)
|
||||
target_link_libraries(dpi_test PRIVATE coop_common)
|
||||
add_test(NAME dpi_test COMMAND dpi_test)
|
||||
|
||||
# Unit test for the audio mixer math (decode/sum/soft-clip/encode). Header-only.
|
||||
add_executable(audio_mix_test audio_mix_test.cpp)
|
||||
target_include_directories(audio_mix_test PRIVATE ${CMAKE_SOURCE_DIR}/host/src)
|
||||
@@ -388,6 +393,7 @@ coop_output_subdir(tests
|
||||
shared_memory_test
|
||||
wav_test
|
||||
tool_paths_test
|
||||
dpi_test
|
||||
mkb_map_test
|
||||
audio_mix_test
|
||||
tone_analysis_test
|
||||
|
||||
52
tests/dpi_test.cpp
Normal file
52
tests/dpi_test.cpp
Normal file
@@ -0,0 +1,52 @@
|
||||
// Unit test for the DPI -> UI scale math (common/include/coop/dpi.hpp): the 96-DPI baseline, common
|
||||
// Windows scalings, the zero-DPI fallback, clamping at both ends, and the default-font pixel-size
|
||||
// derivation. Pure math -- no window, no ImGui -- so it's the reasonably-testable slice of the
|
||||
// per-monitor DPI feature (the actual awareness + font rasterization are verified by hand).
|
||||
#include <cmath>
|
||||
#include <cstdio>
|
||||
|
||||
#include "coop/dpi.hpp"
|
||||
|
||||
using namespace coop;
|
||||
|
||||
namespace
|
||||
{
|
||||
int g_failures = 0;
|
||||
void check(bool ok, const char* what)
|
||||
{
|
||||
std::printf("%s %s\n", ok ? " ok:" : "FAIL:", what);
|
||||
if (!ok)
|
||||
{
|
||||
++g_failures;
|
||||
}
|
||||
}
|
||||
bool approx(float a, float b)
|
||||
{
|
||||
return std::fabs(a - b) < 1e-4f;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
int main()
|
||||
{
|
||||
// Baseline and the common Windows display scalings.
|
||||
check(approx(dpi_scale_from(96), 1.0f), "96 DPI -> 1.0 (100%)");
|
||||
check(approx(dpi_scale_from(120), 1.25f), "120 DPI -> 1.25 (125%)");
|
||||
check(approx(dpi_scale_from(144), 1.5f), "144 DPI -> 1.5 (150%)");
|
||||
check(approx(dpi_scale_from(192), 2.0f), "192 DPI -> 2.0 (200%)");
|
||||
|
||||
// A zero / unknown DPI falls back to the 96 baseline (never 0 -> a zero-size font).
|
||||
check(approx(dpi_scale_from(0), 1.0f), "0 DPI -> 1.0 fallback");
|
||||
|
||||
// Clamping: never below 100%, and capped at the max for an absurd report.
|
||||
check(approx(dpi_scale_from(48), kMinUiScale), "48 DPI clamps up to the min UI scale");
|
||||
check(approx(dpi_scale_from(100000), kMaxUiScale), "absurd DPI clamps to the max UI scale");
|
||||
|
||||
// Font size scales from the single named base constant, matching ImGui's 13px default at 100%.
|
||||
check(approx(kBaseFontPx, 13.0f), "base font px is ImGui's 13px default");
|
||||
check(approx(scaled_font_px(96), 13.0f), "font px at 100% == base");
|
||||
check(approx(scaled_font_px(144), 19.5f), "font px at 150% == 13 * 1.5");
|
||||
check(approx(scaled_font_px(0), kBaseFontPx), "font px at unknown DPI == base (fallback)");
|
||||
|
||||
std::printf(g_failures == 0 ? "PASS dpi_test\n" : "FAILED dpi_test (%d)\n", g_failures);
|
||||
return g_failures == 0 ? 0 : 1;
|
||||
}
|
||||
Reference in New Issue
Block a user