fix(inject/host/windows): eager-create the XUSB pad on Arrival + refresh last_active (G10)

The XUSB manager's `handle` dropped `GamepadEvent::Arrival` via a `let else`, so
the GameStream path never created the pad until the first `State` and missed the
first XInput poll. Match on the event and `ensure` eagerly on Arrival, mirroring
the DualSense backend. Also refresh `last_active` on create and unplug so a
freshly-created pad's residual-rumble idle clock starts fresh rather than
inheriting a stale Instant (which could force off a legitimate rumble at once).

Verified: Windows .173 `cargo clippy -p punktfunk-host --all-targets -- -D warnings` (green).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-07-13 22:05:13 +02:00
parent 31bc863084
commit 2f214532d9
@@ -282,6 +282,7 @@ impl GamepadManager {
); );
self.pads[idx] = Some(p); self.pads[idx] = Some(p);
self.last_rumble[idx] = (0, 0); self.last_rumble[idx] = (0, 0);
self.last_active[idx] = Instant::now();
self.gate.on_success(); self.gate.on_success();
} }
Err(e) => { Err(e) => {
@@ -292,9 +293,12 @@ impl GamepadManager {
} }
pub fn handle(&mut self, ev: &GamepadEvent) { pub fn handle(&mut self, ev: &GamepadEvent) {
let GamepadEvent::State(f) = ev else { match ev {
return; // Arrival metadata — the pad is created lazily on the first State GamepadEvent::Arrival { index, kind, .. } => {
}; tracing::info!(index, kind, "controller arrival (Xbox 360/Windows)");
self.ensure(*index as usize);
}
GamepadEvent::State(f) => {
let idx = f.index.max(0) as usize; let idx = f.index.max(0) as usize;
if idx >= MAX_PADS { if idx >= MAX_PADS {
return; return;
@@ -305,6 +309,7 @@ impl GamepadManager {
tracing::info!(index = i, "controller unplugged (Xbox 360/Windows)"); tracing::info!(index = i, "controller unplugged (Xbox 360/Windows)");
*slot = None; *slot = None;
self.last_rumble[i] = (0, 0); self.last_rumble[i] = (0, 0);
self.last_active[i] = Instant::now();
} }
} }
if f.active_mask & (1 << idx) == 0 { if f.active_mask & (1 << idx) == 0 {
@@ -323,6 +328,8 @@ impl GamepadManager {
); );
} }
} }
}
}
/// Relay any changed rumble level to the client. XUSB motors are 0..255; the wire carries /// Relay any changed rumble level to the client. XUSB motors are 0..255; the wire carries
/// 0..65535, so scale by 257. `large` (low-frequency) → the datagram's `low`, `small` /// 0..65535, so scale by 257. `large` (low-frequency) → the datagram's `low`, `small`