fix(host/windows): force off a game-abandoned rumble on the UMDF virtual pads
The DualSense / DualSense Edge / DualShock 4 / Switch Pro / Steam Deck backends
all run through UhidManager, whose pump() forwarded rumble only on a level
CHANGE and had no idle watchdog. A game that latches a one-shot rumble (a
Stardew axe hit, a DS3 hit) and then stops writing output reports left
last_rumble non-zero; native.rs re-sends the latched level every ~120 ms with a
fresh TTL and the Apple RumbleRenderer refreshes its envelope on every renewal,
so the controller vibrated continuously until a later event happened to write a
report the host parsed as a stop. The XUSB path already guards against this
(RUMBLE_IDLE_TIMEOUT force-off, 19e9828e); that guard was never ported here, so
every UMDF pad regressed for game-abandoned rumble once clients began
negotiating first-class virtual DualSense/DS4/etc. on Windows.
Port the guard into UhidManager::pump, keyed on game ACTIVITY (a fresh output
report, even at an unchanged level) so a rumble the game keeps asserting is
never cut — only an abandoned residual. The activity signal rides a new
PadFeedback.game_drove: Option<bool>; the Windows backends set it from a fresh
out_seq (via a `fresh` flag on DsFeedback/Ds4Feedback; the Deck uses is_some()).
Linux backends leave it None (untracked → always-active → the force-off never
fires there), so their behaviour is unchanged. +2 deterministic unit tests.
Verified: cargo check -p punktfunk-host --tests green on both Windows (.173) and
Linux (home-worker-5); the 10 inject::uhid_manager tests pass on Linux.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -469,6 +469,10 @@ pub struct DsFeedback {
|
||||
pub hidout: Vec<HidOutput>,
|
||||
/// `(low, high)` motor levels (0..=0xFFFF), if a report carried them.
|
||||
pub rumble: Option<(u16, u16)>,
|
||||
/// Whether a fresh output report was seen this poll (set by the backend's section poll, not by
|
||||
/// the parser) — the game-activity signal the [`UhidManager`](crate::inject::uhid_manager)
|
||||
/// abandoned-rumble force-off keys on.
|
||||
pub fresh: bool,
|
||||
}
|
||||
|
||||
/// Parse a DualSense USB output report (`0x02`) into a [`DsFeedback`]. The byte layout below is
|
||||
|
||||
Reference in New Issue
Block a user