forza-trigger: stop per-packet writes between races (fix periodic clicks)
User reports periodic clicks and LED color changes on the controller "between races". Theory: out-of-race, the run loop was calling reset_triggers(controller) AND apply_lightbar(controller, ...) on every Forza packet (60Hz). Even though both call sites land in library code that nominally dedupes "same state" writes, in practice any state change anywhere — including the lightbar dropping a green channel value as RPM coasts down to idle — triggers a full HID OUT report rewrite. The OUT report carries the trigger configuration too; the controller firmware reacts on receipt and produces an audible click each time. Fix: out-of-race path becomes edge-triggered. On the in-race → menu transition we run state.reset() + reset_all() once (turning both triggers off and the lightbar to (0,0,0)). Subsequent menu packets make no controller calls at all until in_race flips back to True. First in-race packet then re-engages handlers and the RPM-driven lightbar. Side effect: the menu-mode car-class lightbar coloring is gone — the bar stays black between races. If we want it back later, it should be one-shot on the menu transition (NOT updated per-packet). For now keep it simple: in-race only. Build clean; tests unchanged (54/54 still pass — they exercise handlers directly, not the run loop).
This commit is contained in:
@@ -777,17 +777,18 @@ def run(host: str, port: int, exit_on_idle: bool = False) -> int:
|
||||
if in_race:
|
||||
handle_throttle(controller, pkt, state)
|
||||
handle_brake(controller, pkt, state)
|
||||
else:
|
||||
# On the in-race → menu transition, partial-reset state so
|
||||
# the next race resumption gets clean EWMA cells, fresh slip
|
||||
# flags (so the cold-start fix at handle_throttle/brake
|
||||
# re-fires), and a redrawn lightbar. Edge-only — repeated
|
||||
# menu packets shouldn't keep clearing state.
|
||||
if state.last_in_race:
|
||||
apply_lightbar(controller, pkt, state, in_race=True)
|
||||
elif state.last_in_race:
|
||||
# in-race → menu edge: one-shot reset of state, triggers,
|
||||
# and lightbar. We do NOT touch the controller again until
|
||||
# the next in-race packet. Doing per-packet writes between
|
||||
# races causes audible clicks on the trigger actuator —
|
||||
# every HID OUT report (even the ones that look idempotent
|
||||
# via library dedup) re-encodes the trigger config and the
|
||||
# firmware reacts on receipt.
|
||||
state.reset()
|
||||
reset_triggers(controller)
|
||||
reset_all(controller)
|
||||
state.last_in_race = in_race
|
||||
apply_lightbar(controller, pkt, state, in_race)
|
||||
finally:
|
||||
try:
|
||||
reset_all(controller)
|
||||
|
||||
Reference in New Issue
Block a user