Run clang-format (the repo's .clang-format: LLVM base, 120 cols, tabs, Allman functions) over every source file so the tree is formatter-clean. Whitespace only -- no behavior change; full x64 + x86 suites pass. Also set SortIncludes: false in .clang-format. Windows include order is load-bearing (windows.h must precede tlhelp32.h / mmreg.h / xinput.h / dinput.h; winsock2.h must precede windows.h), and the default alphabetical sort reorders tlhelp32.h ahead of windows.h -- a build break. Leaving order alone keeps the manual, correct grouping.
144 lines
5.1 KiB
C++
144 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;
|
|
}
|