fix(host/windows): propagate XUSB devnode-create failure instead of latching a phantom pad

XusbWinPad::open swallowed a SwDeviceCreate failure — it returned Ok with
`_sw: None` (a pad with no devnode) and logged only a warn, so PadSlots latched a
phantom pad, called gate.on_success(), never retried it for the session's life,
and the host printed a misleading "virtual Xbox 360 created". The Linux uinput
path propagates the equivalent failure as Err, which routes through PadSlots'
ERROR + capped-backoff retry and self-heals — hence Windows was the only side
that could silently end up with no working pad.

Propagate the create failure with `?` so Windows gets the same ERROR + backoff
retry as Linux. Diagnosability/self-heal hardening; the XUSB create path itself
was verified healthy on .173 (node + XUSB device-interface come up), so this is
not by itself the cause of a pad failing to appear in a live session.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-07-16 17:45:27 +02:00
parent 4ffa2665ac
commit 1a7e3a6e4f
@@ -159,14 +159,13 @@ impl XusbWinPad {
std::ptr::write_unaligned(base.add(OFF_PAD_INDEX) as *mut u32, index as u32);
std::ptr::write_unaligned(base as *mut u32, SHM_MAGIC);
}
let (hsw, instance_id) = match create_swdevice(index) {
Ok((h, id)) => (Some(h), id),
Err(e) => {
tracing::warn!(error = %format!("{e:#}"), "SwDeviceCreate failed; XUSB devnode unavailable");
(None, None)
}
};
let _sw = hsw.map(super::gamepad_raii::SwDevice::new);
// Propagate a devnode-create failure instead of swallowing it: a swallowed failure left the
// pad with no devnode yet still reported success, so PadSlots latched a phantom pad (never
// re-created for the session's life) and the host logged a misleading "virtual Xbox 360
// created". Returning Err routes it through PadSlots' ERROR + capped-backoff retry — parity
// with the Linux uinput path, which self-heals for exactly this reason.
let (hsw, instance_id) = create_swdevice(index)?;
let _sw = Some(super::gamepad_raii::SwDevice::new(hsw));
// Bounded eager delivery: the driver's EvtDeviceAdd publishes its pid right away; handing it
// the DATA handle before we return means the pad is live for the game's first XInput poll.
// On a missing/old driver this waits out the window once and the service pump takes over.