// 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 #include #include // must precede mmreg.h (defines FAR/NEAR it relies on) #include // 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; }