- injector bitness gate: IsWow64Process2 failure was treated as "native", which
would send the x64 DLL into a 32-bit target. Fall back to the legacy
IsWow64Process before giving up to permissive.
- injector helper exit: GetExitCodeProcess's BOOL was ignored; on a failed query
surface GetLastError instead of a misleading exit code.
- window_capture: CreateShaderResourceView's HRESULT was ignored, and width_/
height_ were committed even on failure, so the (latest_ == nullptr) recreate
guard never retried -- a silently black mirror until the next resize. Only
commit the dims on success; otherwise drop latest_ so the next frame retries.
- xinput rumble: make the best-effort XInputSetState ignore explicit (a
disconnected pad re-syncs on the next refresh; the result isn't actionable).
The GetClientRect/ClientToScreen reads in mkb_forward are left as-is: a failure
there is a single self-correcting frame (the mapping is rejected and reused next
frame), so checking them adds no actionable behavior.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The XInput hook used to swallow XInputSetState; now it records the requested
left/right motor speeds into the status back-channel (protocol v8->v9: per-slot
rumble_left/right in HookStatus). Each frame the host reads them and, only on
change, drives the guest's actuator via the active backend:
- XInput: XInputSetState on the guest's slot (the open question is whether Steam's
RPT virtual pad accepts vibration and routes it to the guest -- needs live RPT);
- Steam Input: SteamInput TriggerVibration on the slot's controller handle, with the
XInput fallback for slots Steam isn't driving.
InputSource gains a set_rumble(slot,left,right) hook (default no-op) implemented by
both backends; SteamInputSource now tracks per-slot controller handles + which slots
it drives.
Verified: hook_selftest (x64 + x86) now asserts the hook records the rumble from
XInputSetState into the status; full build x64 + x86 clean; ctest x64 9/9, x86 3/3.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
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>