Scaffold CoopAllTheThings: Remote Play Together for any XInput game via a mirror app under a donor appid (real game keeps its own appid, so DRM, achievements, and playtime stay intact). - Build: CMake skeleton, ImGui + SafetyHook submodules (no vcpkg) - common/: host<->hook IPC contract (seqlock pad state, shared-memory RAII) - host/: borderless D3D11 window + ImGui overlay listing visible XInput pads, behind an InputSource interface (Steam Input slots in later) - README documents the Phase 0 donor-launch validation procedure, anti-cheat limitation, and XInput/bitness constraints Phase 0 validates the riskiest assumption (Steam RPT streams an arbitrary window under a donor appid and routes guest input to it) before capture and injection are built. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
83 lines
2.0 KiB
C++
83 lines
2.0 KiB
C++
#include "debug_overlay.hpp"
|
|
|
|
#include <imgui.h>
|
|
|
|
namespace coop
|
|
{
|
|
|
|
namespace
|
|
{
|
|
|
|
struct ButtonBit
|
|
{
|
|
std::uint16_t mask;
|
|
const char* label;
|
|
};
|
|
|
|
// XINPUT_GAMEPAD_* bit values (kept local so this file needn't include Xinput.h).
|
|
constexpr ButtonBit kButtons[] = {
|
|
{0x0001, "Up"}, {0x0002, "Down"}, {0x0004, "Left"}, {0x0008, "Right"}, {0x0010, "Start"},
|
|
{0x0020, "Back"}, {0x0040, "LS"}, {0x0080, "RS"}, {0x0100, "LB"}, {0x0200, "RB"},
|
|
{0x1000, "A"}, {0x2000, "B"}, {0x4000, "X"}, {0x8000, "Y"},
|
|
};
|
|
|
|
void draw_pad(int index, const PadInfo& pad)
|
|
{
|
|
ImGui::PushID(index);
|
|
if (!pad.connected)
|
|
{
|
|
ImGui::TextDisabled("Slot %d: disconnected", index);
|
|
ImGui::PopID();
|
|
return;
|
|
}
|
|
|
|
ImGui::Text("Slot %d [%s]", index, pad.source.c_str());
|
|
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);
|
|
|
|
bool first = true;
|
|
ImGui::TextUnformatted("Buttons: ");
|
|
for (const ButtonBit& b : kButtons)
|
|
{
|
|
if ((pad.state.buttons & b.mask) != 0)
|
|
{
|
|
ImGui::SameLine();
|
|
ImGui::TextColored(ImVec4(0.4f, 1.0f, 0.4f, 1.0f), "%s%s", first ? "" : ", ", b.label);
|
|
first = false;
|
|
}
|
|
}
|
|
if (first)
|
|
{
|
|
ImGui::SameLine();
|
|
ImGui::TextDisabled("(none)");
|
|
}
|
|
ImGui::Separator();
|
|
ImGui::PopID();
|
|
}
|
|
|
|
} // namespace
|
|
|
|
void draw_debug_overlay(const InputSource& input)
|
|
{
|
|
ImGui::SetNextWindowPos(ImVec2(24, 24), ImGuiCond_FirstUseEver);
|
|
ImGui::SetNextWindowSize(ImVec2(420, 0), ImGuiCond_FirstUseEver);
|
|
ImGui::Begin("CoopAllTheThings - Phase 0 spike");
|
|
|
|
ImGui::Text("Input backend: %s", input.name());
|
|
ImGui::Text("%.1f FPS (%.2f ms)", ImGui::GetIO().Framerate, 1000.0f / ImGui::GetIO().Framerate);
|
|
ImGui::TextDisabled("This window is what Remote Play Together captures.");
|
|
ImGui::TextDisabled("Press Esc to quit.");
|
|
ImGui::Separator();
|
|
|
|
const auto& pads = input.pads();
|
|
for (int i = 0; i < static_cast<int>(pads.size()); ++i)
|
|
{
|
|
draw_pad(i, pads[i]);
|
|
}
|
|
|
|
ImGui::End();
|
|
}
|
|
|
|
} // namespace coop
|