Forward rumble back to the guest controller (both backends)

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>
This commit is contained in:
2026-06-21 05:11:33 +02:00
parent 7673f186db
commit 056a478e19
13 changed files with 108 additions and 13 deletions

View File

@@ -140,6 +140,17 @@ public:
}
}
// Record the rumble the game requested for a slot (so the host can forward it to
// the guest's controller). Plain stores; the hook is the sole writer.
void note_rumble(std::uint32_t slot, std::uint16_t left, std::uint16_t right)
{
if (block_ != nullptr && slot < kMaxPads)
{
block_->status.rumble_left[slot] = left;
block_->status.rumble_right[slot] = right;
}
}
// --- Audio render-hook diagnostics -------------------------------------
// Total distinct render streams the audio hook has observed.