1mod ffi;
115#[cfg(target_os = "linux")]
116mod linux;
117mod map;
118
119use crate::{GamepadEvent, GamepadId, GamepadSnapshot};
120use ffi::{ABS_CNT, EV_ABS, EV_KEY, EV_SYN, InputEvent, KeyBits, SYN_DROPPED, SYN_REPORT};
121#[cfg(target_os = "linux")]
122pub use linux::Evdev;
123use map::{Layout, Probe};
124pub use map::{stick_axis, trigger_axis};
125use std::collections::HashSet;
126use std::io;
127use std::path::{Path, PathBuf};
128use std::time::{Duration, Instant};
129
130#[derive(Clone, Debug, PartialEq, Eq)]
132pub enum EvdevError {
133 Scan {
136 message: String,
138 },
139 Open {
143 path: PathBuf,
145 message: String,
147 },
148 Read {
152 path: PathBuf,
154 message: String,
156 },
157}
158
159impl std::fmt::Display for EvdevError {
160 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
161 match self {
162 Self::Scan { message } => write!(f, "could not list /dev/input: {message}"),
163 Self::Open { path, message } => {
164 write!(f, "could not probe {}: {message}", path.display())
165 }
166 Self::Read { path, message } => {
167 write!(f, "could not read {}: {message}", path.display())
168 }
169 }
170 }
171}
172
173impl std::error::Error for EvdevError {}
174
175pub const RESCAN_INTERVAL: Duration = Duration::from_secs(1);
181
182#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
185struct Node {
186 path: PathBuf,
187 ino: u64,
188}
189
190trait Device {
192 fn probe(&mut self) -> io::Result<Probe>;
194 fn read(&mut self, events: &mut Vec<InputEvent>) -> io::Result<()>;
196}
197
198trait Source {
200 type Device: Device;
202 fn scan(&mut self) -> io::Result<Vec<Node>>;
204 fn open(&mut self, path: &Path) -> io::Result<Self::Device>;
206}
207
208fn skipped_quietly(error: &io::Error) -> bool {
211 matches!(
212 error.kind(),
213 io::ErrorKind::PermissionDenied | io::ErrorKind::NotFound
214 ) || error.raw_os_error() == Some(ffi::ENODEV)
215}
216
217#[derive(Debug)]
219struct Pad<D> {
220 node: Node,
221 device: D,
222 id: GamepadId,
223 layout: Layout,
224 held: KeyBits,
226 values: [i32; ABS_CNT],
228 dropping: bool,
230 last: GamepadSnapshot,
232}
233
234impl<D: Device> Pad<D> {
235 fn take_state(&mut self, probe: &Probe) {
237 self.held = probe.held;
238 self.values = probe.values();
239 }
240
241 fn report(&mut self, emit: &mut impl FnMut(GamepadEvent)) {
243 let snapshot = self.layout.snapshot(&self.held, &self.values);
244 if snapshot != self.last {
245 self.last = snapshot;
246 emit(GamepadEvent::State {
247 id: self.id,
248 snapshot,
249 });
250 }
251 }
252
253 fn pump(
255 &mut self,
256 events: &mut Vec<InputEvent>,
257 emit: &mut impl FnMut(GamepadEvent),
258 ) -> io::Result<()> {
259 events.clear();
260 self.device.read(events)?;
261 for event in events.iter() {
262 match (event.kind, event.code) {
263 (EV_SYN, SYN_DROPPED) => self.dropping = true,
264 (EV_SYN, SYN_REPORT) => {
265 if self.dropping {
266 let probe = self.device.probe()?;
267 self.take_state(&probe);
268 self.dropping = false;
269 }
270 self.report(emit);
271 }
272 _ if self.dropping => {}
273 (EV_KEY, code) => self.held.set(code, event.value != 0),
274 (EV_ABS, code) => {
275 if let Some(value) = self.values.get_mut(usize::from(code)) {
276 *value = event.value;
277 }
278 }
279 _ => {}
280 }
281 }
282 Ok(())
283 }
284}
285
286#[derive(Debug)]
288struct Poller<D> {
289 pads: Vec<Pad<D>>,
290 not_pads: HashSet<Node>,
293 scanned: Option<Instant>,
295 scan_failure: Option<EvdevError>,
299 events: Vec<InputEvent>,
301}
302
303impl<D> Default for Poller<D> {
304 fn default() -> Self {
305 Self {
306 pads: Vec::new(),
307 not_pads: HashSet::new(),
308 scanned: None,
309 scan_failure: None,
310 events: Vec::new(),
311 }
312 }
313}
314
315impl<D: Device> Poller<D> {
316 fn poll<S: Source<Device = D>>(
320 &mut self,
321 source: &mut S,
322 now: Instant,
323 emit: &mut impl FnMut(GamepadEvent),
324 ) -> Result<(), EvdevError> {
325 let mut failure = None;
326 let events = &mut self.events;
327 self.pads.retain_mut(|pad| match pad.pump(events, emit) {
328 Ok(()) => true,
329 Err(error) => {
330 emit(GamepadEvent::Disconnected { id: pad.id });
331 if error.raw_os_error() != Some(ffi::ENODEV) {
332 failure = Some(EvdevError::Read {
333 path: pad.node.path.clone(),
334 message: error.to_string(),
335 });
336 }
337 false
338 }
339 });
340
341 if self
342 .scanned
343 .is_none_or(|at| now.duration_since(at) >= RESCAN_INTERVAL)
344 {
345 self.scanned = Some(now);
346 self.scan_failure = None;
347 match source.scan() {
348 Ok(nodes) => {
349 self.not_pads.retain(|node| nodes.contains(node));
350 for node in nodes {
351 if let Err(error) = self.connect(source, node, emit) {
352 self.scan_failure = Some(error);
353 }
354 }
355 }
356 Err(error) => {
357 self.scan_failure = Some(EvdevError::Scan {
358 message: error.to_string(),
359 });
360 }
361 }
362 }
363 match failure.or_else(|| self.scan_failure.clone()) {
364 Some(failure) => Err(failure),
365 None => Ok(()),
366 }
367 }
368
369 fn connect<S: Source<Device = D>>(
372 &mut self,
373 source: &mut S,
374 node: Node,
375 emit: &mut impl FnMut(GamepadEvent),
376 ) -> Result<(), EvdevError> {
377 if self.not_pads.contains(&node) || self.pads.iter().any(|pad| pad.node == node) {
378 return Ok(());
379 }
380 let opened = source.open(&node.path).and_then(|mut device| {
381 let probe = device.probe()?;
382 Ok((device, probe))
383 });
384 let (device, probe) = match opened {
385 Ok(opened) => opened,
386 Err(error) if skipped_quietly(&error) => return Ok(()),
387 Err(error) => {
388 return Err(EvdevError::Open {
389 path: node.path,
390 message: error.to_string(),
391 });
392 }
393 };
394 if !map::is_gamepad(&probe.keys, &probe.abs) {
395 self.not_pads.insert(node);
396 return Ok(());
397 }
398 let layout = Layout::new(&probe);
399 let id = GamepadId::allocate();
400 let kind = layout.kind;
401 emit(GamepadEvent::Connected { id, kind });
402 let mut pad = Pad {
403 node,
404 device,
405 id,
406 layout,
407 held: KeyBits::EMPTY,
408 values: [0; ABS_CNT],
409 dropping: false,
410 last: GamepadSnapshot::neutral(kind),
411 };
412 pad.take_state(&probe);
413 pad.report(emit);
414 self.pads.push(pad);
415 Ok(())
416 }
417}
418
419#[cfg(test)]
420mod tests {
421 use super::*;
422 use crate::{ActionDecl, ActionKind, ActionMap, Binding, PadAxis, PadButton, PadKind};
423 use ffi::{ABS_X, ABS_Y, BTN_EAST, BTN_SOUTH, ENODEV};
424 use map::tests::{XPAD_STICK, xpad};
425 use std::cell::RefCell;
426 use std::collections::BTreeMap;
427 use std::rc::Rc;
428
429 #[derive(Debug)]
432 struct Script {
433 probe: Probe,
434 pending: Vec<InputEvent>,
435 open_error: Option<io::ErrorKind>,
436 read_error: Option<i32>,
437 probes: u32,
438 }
439
440 type Shared = Rc<RefCell<Script>>;
441
442 #[derive(Debug)]
443 struct FakeDevice(Shared);
444
445 impl Device for FakeDevice {
446 fn probe(&mut self) -> io::Result<Probe> {
447 let mut script = self.0.borrow_mut();
448 script.probes += 1;
449 Ok(script.probe.clone())
450 }
451
452 fn read(&mut self, events: &mut Vec<InputEvent>) -> io::Result<()> {
453 let mut script = self.0.borrow_mut();
454 if let Some(code) = script.read_error {
455 return Err(io::Error::from_raw_os_error(code));
456 }
457 events.append(&mut script.pending);
458 Ok(())
459 }
460 }
461
462 #[derive(Debug, Default)]
465 struct Fake {
466 nodes: BTreeMap<PathBuf, (u64, Shared)>,
467 scans: u32,
468 opens: u32,
469 now: Option<Instant>,
470 }
471
472 impl Fake {
473 fn plug(&mut self, name: &str, ino: u64, probe: Probe) -> Shared {
476 let script = Rc::new(RefCell::new(Script {
477 probe,
478 pending: Vec::new(),
479 open_error: None,
480 read_error: None,
481 probes: 0,
482 }));
483 self.nodes.insert(
484 Path::new("/dev/input").join(name),
485 (ino, Rc::clone(&script)),
486 );
487 script
488 }
489
490 fn unplug(&mut self, name: &str) {
491 self.nodes.remove(&Path::new("/dev/input").join(name));
492 }
493 }
494
495 impl Source for Fake {
496 type Device = FakeDevice;
497
498 fn scan(&mut self) -> io::Result<Vec<Node>> {
499 self.scans += 1;
500 Ok(self
501 .nodes
502 .iter()
503 .map(|(path, (ino, _))| Node {
504 path: path.clone(),
505 ino: *ino,
506 })
507 .collect())
508 }
509
510 fn open(&mut self, path: &Path) -> io::Result<FakeDevice> {
511 self.opens += 1;
512 let (_, script) = self.nodes.get(path).ok_or(io::ErrorKind::NotFound)?;
513 if let Some(kind) = script.borrow().open_error {
514 return Err(kind.into());
515 }
516 Ok(FakeDevice(Rc::clone(script)))
517 }
518 }
519
520 fn poll_after(
522 poller: &mut Poller<FakeDevice>,
523 fake: &mut Fake,
524 after: Duration,
525 ) -> (Vec<GamepadEvent>, Result<(), EvdevError>) {
526 let now = fake.now.map_or_else(Instant::now, |now| now + after);
527 fake.now = Some(now);
528 let mut events = Vec::new();
529 let result = poller.poll(fake, now, &mut |event| events.push(event));
530 (events, result)
531 }
532
533 fn poll(
535 poller: &mut Poller<FakeDevice>,
536 fake: &mut Fake,
537 ) -> (Vec<GamepadEvent>, Result<(), EvdevError>) {
538 poll_after(poller, fake, RESCAN_INTERVAL)
539 }
540
541 const fn key(code: u16, value: i32) -> InputEvent {
542 InputEvent::new(EV_KEY, code, value)
543 }
544 const fn abs(code: u16, value: i32) -> InputEvent {
545 InputEvent::new(EV_ABS, code, value)
546 }
547 const REPORT: InputEvent = InputEvent::new(EV_SYN, SYN_REPORT, 0);
548 const DROPPED: InputEvent = InputEvent::new(EV_SYN, SYN_DROPPED, 0);
549
550 fn keyboard() -> Probe {
552 map::tests::probe(map::tests::XBOX, &[1, 30, 57], &[])
553 }
554
555 #[test]
558 fn transitions_are_reported_once_each() {
559 let mut poller = Poller::default();
560 let mut fake = Fake::default();
561 assert_eq!(poll(&mut poller, &mut fake), (vec![], Ok(())), "no nodes");
562
563 let pad = fake.plug("event3", 10, xpad());
564 let (events, result) = poll(&mut poller, &mut fake);
565 assert_eq!(result, Ok(()));
566 let [GamepadEvent::Connected { id, kind }] = events[..] else {
567 panic!("a pad at rest connects with no state: {events:?}");
568 };
569 assert_eq!(kind, PadKind::Xbox);
570
571 pad.borrow_mut().pending = vec![key(BTN_SOUTH, 1), abs(ABS_Y, -32768), REPORT];
572 let (events, _) = poll_after(&mut poller, &mut fake, Duration::ZERO);
573 let [GamepadEvent::State { id: same, snapshot }] = events[..] else {
574 panic!("one report, one state: {events:?}");
575 };
576 assert_eq!(same, id);
577 assert!(snapshot.buttons.contains(PadButton::South));
578 assert_eq!(snapshot.axis(PadAxis::LeftY), 1.0, "pushed up");
579
580 pad.borrow_mut().pending = vec![key(BTN_SOUTH, 2), REPORT];
581 let (events, _) = poll_after(&mut poller, &mut fake, Duration::ZERO);
582 assert!(events.is_empty(), "autorepeat changes nothing: {events:?}");
583
584 pad.borrow_mut().pending = vec![key(BTN_SOUTH, 0), abs(ABS_Y, 0)];
585 let (events, _) = poll_after(&mut poller, &mut fake, Duration::ZERO);
586 assert!(
587 events.is_empty(),
588 "nothing before the SYN_REPORT: {events:?}"
589 );
590 pad.borrow_mut().pending = vec![REPORT];
591 let (events, _) = poll_after(&mut poller, &mut fake, Duration::ZERO);
592 assert_eq!(
593 events,
594 [GamepadEvent::State {
595 id,
596 snapshot: GamepadSnapshot::neutral(PadKind::Xbox),
597 }]
598 );
599
600 pad.borrow_mut().read_error = Some(ENODEV);
601 fake.unplug("event3");
602 assert_eq!(
603 poll(&mut poller, &mut fake),
604 (vec![GamepadEvent::Disconnected { id }], Ok(())),
605 "ENODEV is an unplug, not an error"
606 );
607
608 fake.plug("event3", 11, xpad());
609 let (events, _) = poll(&mut poller, &mut fake);
610 let [GamepadEvent::Connected { id: again, .. }] = events[..] else {
611 panic!("{events:?}");
612 };
613 assert_ne!(again, id, "a new node is a new pad");
614 }
615
616 #[test]
618 fn a_pad_connected_mid_press_reports_its_state() {
619 let mut held = xpad();
620 held.held.set(BTN_EAST, true);
621 held.axes[usize::from(ABS_X)].value = XPAD_STICK.1;
622 let mut poller = Poller::default();
623 let mut fake = Fake::default();
624 fake.plug("event0", 1, held);
625 let (events, _) = poll(&mut poller, &mut fake);
626 let [
627 GamepadEvent::Connected { id, .. },
628 GamepadEvent::State { id: same, snapshot },
629 ] = events[..]
630 else {
631 panic!("{events:?}");
632 };
633 assert_eq!(same, id);
634 assert!(snapshot.buttons.contains(PadButton::East));
635 assert_eq!(snapshot.axis(PadAxis::LeftX), 1.0);
636 }
637
638 #[test]
641 fn an_unexpected_read_error_disconnects_and_is_reported() {
642 const EIO: i32 = 5;
643 let mut poller = Poller::default();
644 let mut fake = Fake::default();
645 let broken = fake.plug("event0", 1, xpad());
646 let fine = fake.plug("event1", 2, xpad());
647 let (events, _) = poll(&mut poller, &mut fake);
648 let [
649 GamepadEvent::Connected { id: first, .. },
650 GamepadEvent::Connected { id: second, .. },
651 ] = events[..]
652 else {
653 panic!("{events:?}");
654 };
655
656 broken.borrow_mut().read_error = Some(EIO);
657 fine.borrow_mut().pending = vec![key(BTN_SOUTH, 1), REPORT];
658 let (events, result) = poll_after(&mut poller, &mut fake, Duration::ZERO);
659 assert_eq!(events[0], GamepadEvent::Disconnected { id: first });
660 assert!(
661 matches!(events[1], GamepadEvent::State { id, .. } if id == second),
662 "{events:?}"
663 );
664 let Err(EvdevError::Read { path, .. }) = result else {
665 panic!("{result:?}");
666 };
667 assert_eq!(path, Path::new("/dev/input/event0"));
668 }
669
670 #[test]
673 fn a_keyboard_is_not_a_pad_and_is_not_reopened() {
674 let mut poller = Poller::default();
675 let mut fake = Fake::default();
676 let keys = fake.plug("event0", 1, keyboard());
677 fake.plug("event1", 2, xpad());
678 let (events, _) = poll(&mut poller, &mut fake);
679 assert!(
680 matches!(events[..], [GamepadEvent::Connected { .. }]),
681 "only the pad: {events:?}"
682 );
683 assert_eq!(fake.opens, 2);
684 for _ in 0..3 {
685 poll(&mut poller, &mut fake).1.expect("scripted");
686 }
687 assert_eq!(fake.scans, 4);
688 assert_eq!(fake.opens, 2, "neither node is opened again");
689 assert_eq!(keys.borrow().probes, 1);
690 }
691
692 #[test]
695 fn a_reused_node_name_is_probed_again() {
696 let mut poller = Poller::default();
697 let mut fake = Fake::default();
698 fake.plug("event0", 1, keyboard());
699 assert_eq!(poll(&mut poller, &mut fake).0, []);
700 fake.plug("event0", 2, xpad());
701 let (events, _) = poll(&mut poller, &mut fake);
702 assert!(
703 matches!(events[..], [GamepadEvent::Connected { .. }]),
704 "{events:?}"
705 );
706 }
707
708 #[test]
712 fn an_unreadable_node_is_skipped_and_retried() {
713 let mut poller = Poller::default();
714 let mut fake = Fake::default();
715 let pad = fake.plug("event0", 1, xpad());
716 pad.borrow_mut().open_error = Some(io::ErrorKind::PermissionDenied);
717 assert_eq!(poll(&mut poller, &mut fake), (vec![], Ok(())));
718 assert_eq!(poll(&mut poller, &mut fake), (vec![], Ok(())));
719 assert_eq!(fake.opens, 2, "retried at the second scan");
720
721 pad.borrow_mut().open_error = Some(io::ErrorKind::OutOfMemory);
722 let (events, result) = poll(&mut poller, &mut fake);
723 assert!(events.is_empty());
724 assert!(matches!(result, Err(EvdevError::Open { .. })), "{result:?}");
725 let (_, between) = poll_after(&mut poller, &mut fake, Duration::ZERO);
726 assert_eq!(between, result, "still failing between scans");
727
728 pad.borrow_mut().open_error = None;
729 let (events, result) = poll(&mut poller, &mut fake);
730 assert_eq!(result, Ok(()));
731 assert!(matches!(events[..], [GamepadEvent::Connected { .. }]));
732 }
733
734 #[test]
737 fn scans_are_throttled_and_pads_are_read_every_poll() {
738 let mut poller = Poller::default();
739 let mut fake = Fake::default();
740 let pad = fake.plug("event0", 1, xpad());
741 poll(&mut poller, &mut fake).1.expect("scripted");
742 assert_eq!(fake.scans, 1);
743
744 fake.plug("event1", 2, xpad());
745 let almost = RESCAN_INTERVAL - Duration::from_millis(1);
746 pad.borrow_mut().pending = vec![key(BTN_SOUTH, 1), REPORT];
747 let (events, _) = poll_after(&mut poller, &mut fake, almost);
748 assert!(
749 matches!(events[..], [GamepadEvent::State { .. }]),
750 "the open pad is read, the new node not yet seen: {events:?}"
751 );
752 assert_eq!(fake.scans, 1);
753
754 let (events, _) = poll_after(&mut poller, &mut fake, Duration::from_millis(1));
755 assert_eq!(fake.scans, 2, "scanned once the interval passed");
756 assert!(
757 matches!(events[..], [GamepadEvent::Connected { .. }]),
758 "{events:?}"
759 );
760 }
761
762 #[test]
765 fn a_dropped_buffer_resynchronises_from_the_device() {
766 let mut poller = Poller::default();
767 let mut fake = Fake::default();
768 let pad = fake.plug("event0", 1, xpad());
769 let (events, _) = poll(&mut poller, &mut fake);
770 let [GamepadEvent::Connected { id, .. }] = events[..] else {
771 panic!("{events:?}");
772 };
773
774 pad.borrow_mut().probe.held.set(BTN_EAST, true);
776 pad.borrow_mut().pending = vec![DROPPED, key(BTN_SOUTH, 1), REPORT];
777 let (events, _) = poll_after(&mut poller, &mut fake, Duration::ZERO);
778 let [GamepadEvent::State { id: same, snapshot }] = events[..] else {
779 panic!("{events:?}");
780 };
781 assert_eq!(same, id);
782 assert!(snapshot.buttons.contains(PadButton::East), "re-read");
783 assert!(!snapshot.buttons.contains(PadButton::South), "discarded");
784 assert_eq!(
785 pad.borrow().probes,
786 2,
787 "one probe to connect, one to resync"
788 );
789
790 pad.borrow_mut().pending = vec![key(BTN_SOUTH, 1), REPORT];
791 let (events, _) = poll_after(&mut poller, &mut fake, Duration::ZERO);
792 let [GamepadEvent::State { snapshot, .. }] = events[..] else {
793 panic!("{events:?}");
794 };
795 assert!(
796 snapshot.buttons.contains(PadButton::South),
797 "read normally again"
798 );
799 }
800
801 #[test]
803 fn polled_events_drive_an_action_map() {
804 let mut map = ActionMap::new();
805 map.declare(ActionDecl {
806 name: "jump".to_owned(),
807 kind: ActionKind::Button,
808 bindings: vec![Binding::PadButton(PadButton::South)],
809 });
810 let mut poller = Poller::default();
811 let mut fake = Fake::default();
812 let pad = fake.plug("event0", 1, xpad());
813 pad.borrow_mut().probe.held.set(BTN_SOUTH, true);
814 let now = Instant::now();
815 poller
816 .poll(&mut fake, now, &mut |event| map.gamepad_event(&event))
817 .expect("scripted");
818 assert!(map.just_pressed("jump"));
819
820 pad.borrow_mut().read_error = Some(ENODEV);
821 poller
822 .poll(&mut fake, now, &mut |event| map.gamepad_event(&event))
823 .expect("scripted");
824 assert!(map.just_released("jump"), "unplugging releases");
825 }
826
827 #[test]
828 fn errors_name_what_failed() {
829 let error = EvdevError::Read {
830 path: PathBuf::from("/dev/input/event4"),
831 message: "Input/output error".to_owned(),
832 };
833 assert_eq!(
834 error.to_string(),
835 "could not read /dev/input/event4: Input/output error"
836 );
837 }
838}