From 8f47f4f12c598ed525e518be12ec99742d56c1ae Mon Sep 17 00:00:00 2001 From: hadelan Date: Sat, 12 Sep 2026 13:59:57 +0800 Subject: [PATCH] padd: only the driving pad's buttons may act MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit With two pads connected, the event drain matched no pad id: a Start or Select from the second pad toggled the policy or shut the robot down while the sticks it combined with were the first pad's. The driving pad is the first one; events from any other are skipped, and after the drain the pad is re-read by id — a Disconnected dequeued this tick must yield one tick of "pad gone", not a silent handover to the other pad's sticks. --- padd/src/main.rs | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/padd/src/main.rs b/padd/src/main.rs index c45ede70..07131851 100644 --- a/padd/src/main.rs +++ b/padd/src/main.rs @@ -479,6 +479,11 @@ fn main() -> std::process::ExitCode { // Drain the queue so axis polling below sees present state, and catch button // *edges* — a held Start must toggle once, not fifty times a second. + // + // The driving pad is the first one, and only its events may act: with two pads + // connected, a Start or Select from the *other* one would otherwise steer a robot + // whose sticks belong to somebody else. + let pad_id = gilrs.gamepads().next().map(|(id, _)| id); let mut toggle_enable = false; let mut toggle_head = false; let mut toggle_body = false; @@ -489,6 +494,9 @@ fn main() -> std::process::ExitCode { let mut select_released = false; let mut reboot_motors = false; while let Some(event) = gilrs.next_event() { + if Some(event.id) != pad_id { + continue; + } // Select is the one button read on its release: a short press stops, a long hold shuts // down, and which it was is only known when the thumb comes off. if let gilrs::EventType::ButtonReleased(Button::Select, _) = event.event { @@ -520,7 +528,10 @@ fn main() -> std::process::ExitCode { } } - let Some((_, pad)) = gilrs.gamepads().next() else { + // Re-read the pad by id after the drain: a `Disconnected` dequeued above may have + // taken it, and asking for the first pad again would silently hand the robot the + // *other* pad's sticks. One tick of "pad gone" beats that. + let Some(pad) = pad_id.and_then(|id| gilrs.connected_gamepad(id)) else { // No pad. Send nothing: `robotd`'s deadman stops the robot on its own, which is // exactly the wanted behaviour, and inventing a zero command here would mask a // disconnected pad as a deliberate stop.