Files
CoopAllTheThings/tests/ui_fit_test.cpp
BlackMark cf7e0c783a M1: UI-fit instrumentation + headless ui_fit_test + first overflow fixes
Add panel overflow instrumentation (record_panel_fit reading ImGui ScrollMax),
a forced layout-reference + debug-aware center split, and host harness commands
(uisize/uifit). New headless ui_fit_test drives the real Controllers + Audio
panels at reference resolutions with Debug details on and asserts no panel
overflows its assigned size; the Audio panel gains a demo mode so its richest
content renders without a live mirror.

Fixes from the measured overflow: widen the center column (was too narrow ->
horizontal overflow), merge the Controllers poll/round-trip tables and fold the
trigger line into the slot line, and make the center height split
Debug-details-aware (Video's height is mirroring-driven, not debug-driven, so a
static split can't serve both modes). Controllers + Audio now fit at 1920x1080
with max info. Video/Injection/Log fit is finalized at the end via the live
uifit harness (M1 stays open until then).

15/15 ctest.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-22 11:14:31 +02:00

150 lines
5.1 KiB
C++

// Headless UI-fit check (M1).
//
// The overlay panels open at fixed sizes that scale with the monitor; with Debug details
// on (maximum information) a panel's content can exceed its assigned size and get scrolled
// out of view. This test drives the *real* Controllers and Audio panels (the tightest
// center-column panels) at reference resolutions with debug details on and the richest
// content, and asserts none overflow its assigned size. Pure ImGui layout -- no GPU, no
// window -- so it runs anywhere. The Audio panel is driven in its demo mode (synthetic
// mirror values) so the full content path renders without a live audio device.
//
// The Video/Injection/Log panels need a live D3D device / IPC channel and are verified via
// the host's debug harness ("uisize"/"uifit") rather than headlessly here.
#include <cstdint>
#include <cstdio>
#include <windows.h> // must precede mmreg.h (defines FAR/NEAR it relies on)
#include <mmreg.h> // WAVE_FORMAT_IEEE_FLOAT
#include "imgui.h"
#include "audio_panel.hpp"
#include "controllers_panel.hpp"
#include "input/input_source.hpp"
#include "ipc/ipc_server.hpp"
#include "ui/app_chrome.hpp"
using namespace coop;
namespace
{
// Populate the hook back-channel with the worst case for the panels: every pad busy, and
// the maximum number of render streams, each low-confidence (the longest provenance label)
// at 8ch / 32-bit / float (the widest format string).
void fill_max_status(HookStatusView& st)
{
st.attached = true;
st.focus_spoof = true;
st.heartbeat = 123456;
for (std::uint32_t i = 0; i < kMaxPads; ++i)
{
st.get_state[i] = 9876543;
st.get_caps[i] = 4242;
st.rumble_left[i] = 65535;
st.rumble_right[i] = 65535;
st.read_state[i].connected = 1;
st.read_state[i].buttons = 0xFFFF;
st.read_state[i].thumb_lx = -12345;
st.read_state[i].thumb_ly = 23456;
}
st.game_pid = 4242;
st.game_hwnd = 0x12345678;
st.audio_streams_seen = kMaxAudioStreams + 5; // exercises the "(showing first N)" note
for (std::uint32_t i = 0; i < kMaxAudioStreams; ++i)
{
AudioStreamInfo& s = st.audio_streams[i];
s.is_primary = (i == 0) ? 1u : 0u;
s.sample_rate = 192000;
s.channels = 8;
s.bits = 32;
s.format_tag = WAVE_FORMAT_IEEE_FLOAT;
s.frames_rendered = 1234567890123ull;
s.format_state = AudioFormat_LowConfidence;
}
}
void fill_max_input(InputSnapshot& in)
{
for (std::uint32_t i = 0; i < kMaxPads; ++i)
{
in.pads[i].connected = true;
in.pads[i].source = "XInput (RPT guest)";
in.pads[i].state.connected = 1;
in.pads[i].state.buttons = 0xFFFF;
in.pads[i].state.thumb_lx = -32768;
in.pads[i].state.thumb_ly = 32767;
in.pads[i].state.left_trigger = 255;
in.pads[i].state.right_trigger = 255;
}
in.backend = "XInput";
}
// Drive the panels for a few frames at a reference size and report the worst overflow.
// ImGui computes a window's ScrollMax in Begin() from the *previous* frame's content size,
// so the reading is only stable from the second frame on -- run several.
bool run_size(float w, float h, AudioPanel& audio, ControllersPanel& controllers, const HookStatusView& st,
const InputSnapshot& in, bool required)
{
ImGui::GetIO().DisplaySize = ImVec2(w, h);
set_layout_reference(w, h);
set_layout_debug(true); // we drive the panels with Debug details on
for (int frame = 0; frame < 4; ++frame)
{
ImGui::GetIO().DeltaTime = 1.0f / 60.0f;
ImGui::NewFrame();
reset_panel_fit();
controllers.draw(in, st, /*debug_details=*/true);
audio.draw_ui(st, /*debug_details=*/true);
ImGui::Render();
}
float ox = 0.0f, oy = 0.0f;
const bool over = panel_fit_overflow(&ox, &oy);
char report[256];
panel_fit_report(report, sizeof(report));
std::printf(" %4.0fx%-4.0f %-9s %-28s (worst %.0f x, %.0f y)%s\n", w, h, over ? "OVERFLOW" : "fit",
report, ox, oy, (over && !required) ? " [best-effort]" : "");
return required ? !over : true;
}
} // namespace
int main()
{
IMGUI_CHECKVERSION();
ImGui::CreateContext();
ImGuiIO& io = ImGui::GetIO();
io.IniFilename = nullptr; // never touch a .ini
io.LogFilename = nullptr;
unsigned char* px = nullptr;
int tw = 0, th = 0;
io.Fonts->GetTexDataAsRGBA32(&px, &tw, &th); // build the default font atlas
io.Fonts->SetTexID((ImTextureID)1); // non-null so Render() is happy; no real backend
HookStatusView st{};
fill_max_status(st);
InputSnapshot in{};
fill_max_input(in);
AudioPanel audio;
audio.dev_set_demo(true); // render the richest content with synthetic mirror values
ControllersPanel controllers;
std::printf("ui_fit_test: panels must fit their assigned size at max info (debug details on)\n");
bool ok = true;
ok &= run_size(1920, 1080, audio, controllers, st, in, /*required=*/true);
// Smaller resolutions are reported but not required to pass (the host targets >=1080p).
run_size(1600, 900, audio, controllers, st, in, /*required=*/false);
run_size(1366, 768, audio, controllers, st, in, /*required=*/false);
ImGui::DestroyContext();
if (!ok)
{
std::printf("FAIL: a panel overflows its assigned size at 1920x1080 with Debug details on.\n");
return 1;
}
std::printf("PASS: Controllers + Audio fit at 1920x1080.\n");
return 0;
}