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>
This commit is contained in:
@@ -37,6 +37,12 @@ void draw_pad(int index, const PadInfo& pad, bool debug_details)
|
||||
}
|
||||
|
||||
ImGui::TextColored(kGreen, "Slot %d [%s]", index, pad.source.c_str());
|
||||
if (debug_details)
|
||||
{
|
||||
// Triggers on the slot line (saves a row); thumbsticks below.
|
||||
ImGui::SameLine();
|
||||
ImGui::TextDisabled("LT %3u RT %3u", pad.state.left_trigger, pad.state.right_trigger);
|
||||
}
|
||||
|
||||
bool first = true;
|
||||
ImGui::TextUnformatted("Buttons: ");
|
||||
@@ -57,7 +63,6 @@ void draw_pad(int index, const PadInfo& pad, bool debug_details)
|
||||
|
||||
if (debug_details)
|
||||
{
|
||||
ImGui::Text("LT %3u RT %3u", pad.state.left_trigger, pad.state.right_trigger);
|
||||
ImGui::Text("L (%6d, %6d) R (%6d, %6d)", pad.state.thumb_lx, pad.state.thumb_ly,
|
||||
pad.state.thumb_rx, pad.state.thumb_ry);
|
||||
}
|
||||
@@ -117,6 +122,7 @@ void ControllersPanel::draw(const InputSnapshot& input, const HookStatusView& st
|
||||
if (!status.attached)
|
||||
{
|
||||
ImGui::TextDisabled("Not injected (no XInput hook).");
|
||||
record_panel_fit("Controllers");
|
||||
ImGui::End();
|
||||
return;
|
||||
}
|
||||
@@ -150,16 +156,24 @@ void ControllersPanel::draw(const InputSnapshot& input, const HookStatusView& st
|
||||
ImGui::TextColored(kGrey, "Game reading controller: idle");
|
||||
}
|
||||
|
||||
// One per-slot table covers both the hook's poll counters and the input round-trip
|
||||
// (what we forwarded vs what the game read back through the hook). A round-trip mismatch
|
||||
// isolates a tool->game forwarding problem from an input->tool one. Merged into a single
|
||||
// table so the (debug) controller view stays inside its panel even with every slot busy.
|
||||
if (debug_details &&
|
||||
ImGui::BeginTable("slots", 4, ImGuiTableFlags_Borders | ImGuiTableFlags_SizingStretchProp))
|
||||
ImGui::BeginTable("slots", 5, ImGuiTableFlags_Borders | ImGuiTableFlags_SizingStretchProp))
|
||||
{
|
||||
ImGui::TableSetupColumn("Slot");
|
||||
ImGui::TableSetupColumn("GetState/s");
|
||||
ImGui::TableSetupColumn("GetState total");
|
||||
ImGui::TableSetupColumn("GetCaps total");
|
||||
ImGui::TableSetupColumn("Poll/s");
|
||||
ImGui::TableSetupColumn("Polls");
|
||||
ImGui::TableSetupColumn("Forwarded btn/LX,LY");
|
||||
ImGui::TableSetupColumn("Game read btn/LX,LY");
|
||||
ImGui::TableHeadersRow();
|
||||
const auto& fwd = input.pads;
|
||||
for (int i = 0; i < static_cast<int>(kMaxPads); ++i)
|
||||
{
|
||||
const CoopPadState& f = fwd[i].state;
|
||||
const CoopPadState& r = status.read_state[i];
|
||||
ImGui::TableNextRow();
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Text("%d", i);
|
||||
@@ -175,42 +189,15 @@ void ControllersPanel::draw(const InputSnapshot& input, const HookStatusView& st
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Text("%llu", static_cast<unsigned long long>(status.get_state[i]));
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Text("%llu", static_cast<unsigned long long>(status.get_caps[i]));
|
||||
ImGui::Text("0x%04X %6d,%6d", f.buttons, f.thumb_lx, f.thumb_ly);
|
||||
ImGui::TableNextColumn();
|
||||
const bool match = f.buttons == r.buttons && f.thumb_lx == r.thumb_lx && f.thumb_ly == r.thumb_ly;
|
||||
ImGui::TextColored(match ? kGreen : kGrey, "0x%04X %6d,%6d", r.buttons, r.thumb_lx, r.thumb_ly);
|
||||
}
|
||||
ImGui::EndTable();
|
||||
}
|
||||
|
||||
// Round-trip view: what we forwarded (the active backend's pad, tagged per slot in
|
||||
// the Incoming section above) vs what the game actually read back through the hook.
|
||||
// A mismatch isolates a tool->game forwarding problem from an input->tool one.
|
||||
if (debug_details)
|
||||
{
|
||||
ImGui::SeparatorText("Round-trip (forwarded vs game read)");
|
||||
if (ImGui::BeginTable("roundtrip", 3, ImGuiTableFlags_Borders | ImGuiTableFlags_SizingStretchProp))
|
||||
{
|
||||
ImGui::TableSetupColumn("Slot");
|
||||
ImGui::TableSetupColumn("Forwarded btn / LX,LY");
|
||||
ImGui::TableSetupColumn("Game read btn / LX,LY");
|
||||
ImGui::TableHeadersRow();
|
||||
const auto& fwd = input.pads;
|
||||
for (int i = 0; i < static_cast<int>(kMaxPads); ++i)
|
||||
{
|
||||
const CoopPadState& f = fwd[i].state;
|
||||
const CoopPadState& r = status.read_state[i];
|
||||
ImGui::TableNextRow();
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Text("%d", i);
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Text("0x%04X %6d,%6d", f.buttons, f.thumb_lx, f.thumb_ly);
|
||||
ImGui::TableNextColumn();
|
||||
const bool match = f.buttons == r.buttons && f.thumb_lx == r.thumb_lx && f.thumb_ly == r.thumb_ly;
|
||||
ImGui::TextColored(match ? kGreen : kGrey, "0x%04X %6d,%6d", r.buttons, r.thumb_lx, r.thumb_ly);
|
||||
}
|
||||
ImGui::EndTable();
|
||||
}
|
||||
ImGui::TextDisabled("Slot tags in 'Incoming' above show which backend (XInput / Steam) fed each slot.");
|
||||
}
|
||||
|
||||
record_panel_fit("Controllers");
|
||||
ImGui::End();
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user