1use super::ff::Device as FfDevice;
9use super::ioctl;
10use super::ioctl::{input_absinfo, input_event};
11use super::udev::*;
12use crate::utils;
13use crate::{AxisInfo, Event, EventType};
14use crate::{PlatformError, PowerInfo};
15
16use libc as c;
17use uuid::Uuid;
18use vec_map::VecMap;
19
20use inotify::{EventMask, Inotify, WatchMask};
21use nix::errno::Errno;
22use nix::sys::epoll::{Epoll, EpollCreateFlags, EpollEvent, EpollFlags, EpollTimeout};
23use nix::sys::eventfd::{EfdFlags, EventFd};
24use std::collections::VecDeque;
25use std::error;
26use std::ffi::OsStr;
27use std::ffi::{CStr, CString};
28use std::fmt::{Display, Formatter, Result as FmtResult};
29use std::fs::File;
30use std::mem::{self, MaybeUninit};
31use std::ops::Index;
32use std::os::raw::c_char;
33use std::os::unix::ffi::OsStrExt;
34use std::os::unix::io::{BorrowedFd, RawFd};
35use std::path::{Path, PathBuf};
36use std::str;
37use std::sync::mpsc;
38use std::sync::mpsc::{Receiver, Sender};
39use std::time::{Duration, SystemTime, UNIX_EPOCH};
40
41const HOTPLUG_DATA: u64 = u64::MAX;
42
43#[derive(Debug)]
44pub struct Gilrs {
45 gamepads: Vec<Gamepad>,
46 epoll: Epoll,
47 hotplug_rx: Receiver<HotplugEvent>,
48 to_check: VecDeque<usize>,
49 discovery_backend: DiscoveryBackend,
50}
51
52#[derive(Debug, Clone, Copy)]
53enum DiscoveryBackend {
54 Udev,
55 Inotify,
56}
57
58const INPUT_DIR_PATH: &str = "/dev/input";
59
60impl Gilrs {
61 pub(crate) fn new() -> Result<Self, PlatformError> {
62 let mut gamepads = Vec::new();
63 let epoll = Epoll::new(EpollCreateFlags::empty())
64 .map_err(|e| errno_to_platform_error(e, "creating epoll fd"))?;
65
66 let mut hotplug_event = EventFd::from_value_and_flags(1, EfdFlags::EFD_NONBLOCK)
67 .map_err(|e| errno_to_platform_error(e, "creating eventfd"))?;
68 epoll
69 .add(
70 &hotplug_event,
71 EpollEvent::new(EpollFlags::EPOLLIN | EpollFlags::EPOLLET, HOTPLUG_DATA),
72 )
73 .map_err(|e| errno_to_platform_error(e, "adding evevntfd do epoll"))?;
74
75 if Path::new("/.flatpak-info").exists() || std::env::var("GILRS_DISABLE_UDEV").is_ok() {
76 log::debug!("Looks like we're in an environment without udev. Falling back to inotify");
77 let (hotplug_tx, hotplug_rx) = mpsc::channel();
78 let mut inotify = Inotify::init().map_err(|err| PlatformError::Other(Box::new(err)))?;
79 let input_dir = Path::new(INPUT_DIR_PATH);
80 inotify
81 .watches()
82 .add(
83 input_dir,
84 WatchMask::CREATE | WatchMask::DELETE | WatchMask::MOVE | WatchMask::ATTRIB,
85 )
86 .map_err(|err| PlatformError::Other(Box::new(err)))?;
87
88 for entry in input_dir
89 .read_dir()
90 .map_err(|err| PlatformError::Other(Box::new(err)))?
91 .flatten()
92 {
93 let file_name = match entry.file_name().into_string() {
94 Ok(file_name) => file_name,
95 Err(_) => continue,
96 };
97 let (gamepad_path, syspath) = match get_gamepad_path(&file_name) {
98 Some((gamepad_path, syspath)) => (gamepad_path, syspath),
99 None => continue,
100 };
101 let devpath = CString::new(gamepad_path.to_str().unwrap()).unwrap();
102 if let Some(gamepad) = Gamepad::open(&devpath, &syspath, DiscoveryBackend::Inotify)
103 {
104 let idx = gamepads.len();
105 gamepad
106 .register_fd(&epoll, idx as u64)
107 .map_err(|e| errno_to_platform_error(e, "registering gamepad in epoll"))?;
108 gamepads.push(gamepad);
109 }
110 }
111
112 std::thread::Builder::new()
113 .name("gilrs".to_owned())
114 .spawn(move || {
115 let mut buffer = [0u8; 1024];
116 debug!("Started gilrs inotify thread");
117 loop {
118 let events = match inotify.read_events_blocking(&mut buffer) {
119 Ok(events) => events,
120 Err(err) => {
121 error!("Failed to check for changes to joysticks: {err}");
122 return;
123 }
124 };
125 for event in events {
126 if !handle_inotify(&hotplug_tx, event, &mut hotplug_event) {
127 return;
128 }
129 }
130 }
131 })
132 .expect("failed to spawn thread");
133 return Ok(Gilrs {
134 gamepads,
135 epoll,
136 hotplug_rx,
137 to_check: VecDeque::new(),
138 discovery_backend: DiscoveryBackend::Inotify,
139 });
140 }
141 let udev = match Udev::new() {
142 Some(udev) => udev,
143 None => {
144 return Err(PlatformError::Other(Box::new(Error::UdevCtx)));
145 }
146 };
147 let en = match udev.enumerate() {
148 Some(en) => en,
149 None => {
150 return Err(PlatformError::Other(Box::new(Error::UdevEnumerate)));
151 }
152 };
153
154 unsafe { en.add_match_property(cstr_new(b"ID_INPUT_JOYSTICK\0"), cstr_new(b"1\0")) }
155 unsafe { en.add_match_subsystem(cstr_new(b"input\0")) }
156 en.scan_devices();
157
158 for dev in en.iter() {
159 if let Some(dev) = Device::from_syspath(&udev, &dev) {
160 let devpath = match dev.devnode() {
161 Some(devpath) => devpath,
162 None => continue,
163 };
164 let syspath = Path::new(OsStr::from_bytes(
165 dev.syspath()
166 .ok_or(PlatformError::Other(Error::NullSyspath.into()))?
167 .to_bytes(),
168 ));
169 if let Some(gamepad) = Gamepad::open(devpath, syspath, DiscoveryBackend::Udev) {
170 let idx = gamepads.len();
171 gamepad
172 .register_fd(&epoll, idx as u64)
173 .map_err(|e| errno_to_platform_error(e, "registering gamepad in epoll"))?;
174 gamepads.push(gamepad);
175 }
176 }
177 }
178
179 let (hotplug_tx, hotplug_rx) = mpsc::channel();
180 std::thread::Builder::new()
181 .name("gilrs".to_owned())
182 .spawn(move || {
183 let udev = match Udev::new() {
184 Some(udev) => udev,
185 None => {
186 error!("Failed to create udev for hot plug thread!");
187 return;
188 }
189 };
190
191 let monitor = match Monitor::new(&udev) {
192 Some(m) => m,
193 None => {
194 error!("Failed to create udev monitor for hot plug thread!");
195 return;
196 }
197 };
198
199 handle_hotplug(hotplug_tx, monitor, hotplug_event)
200 })
201 .expect("failed to spawn thread");
202
203 Ok(Gilrs {
204 gamepads,
205 epoll,
206 hotplug_rx,
207 to_check: VecDeque::new(),
208 discovery_backend: DiscoveryBackend::Udev,
209 })
210 }
211
212 pub(crate) fn next_event(&mut self) -> Option<Event> {
213 self.next_event_impl(Some(Duration::new(0, 0)))
214 }
215
216 pub(crate) fn next_event_blocking(&mut self, timeout: Option<Duration>) -> Option<Event> {
217 self.next_event_impl(timeout)
218 }
219
220 fn next_event_impl(&mut self, timeout: Option<Duration>) -> Option<Event> {
221 let mut check_hotplug = false;
222
223 if self.to_check.is_empty() {
224 let mut events = [EpollEvent::new(EpollFlags::empty(), 0); 16];
225 let timeout = if let Some(timeout) = timeout {
226 EpollTimeout::try_from(timeout).expect("timeout too large")
227 } else {
228 EpollTimeout::NONE
229 };
230
231 let n = match self.epoll.wait(&mut events, timeout) {
232 Ok(n) => n,
233 Err(e) => {
234 error!("epoll failed: {}", e);
235 return None;
236 }
237 };
238
239 if n == 0 {
240 return None;
241 }
242
243 for event in events {
244 if event.events().contains(EpollFlags::EPOLLIN) {
245 if event.data() == HOTPLUG_DATA {
246 check_hotplug = true;
247 } else {
248 self.to_check.push_back(event.data() as usize);
249 }
250 }
251 }
252 }
253
254 if check_hotplug {
255 if let Some(event) = self.handle_hotplug() {
256 return Some(event);
257 }
258 }
259
260 while let Some(idx) = self.to_check.front().copied() {
261 let gamepad = match self.gamepads.get_mut(idx) {
262 Some(gp) => gp,
263 None => {
264 warn!("Somehow got invalid index from event");
265 self.to_check.pop_front();
266 return None;
267 }
268 };
269
270 if !gamepad.is_connected {
271 self.to_check.pop_front();
272 continue;
273 }
274
275 match gamepad.event() {
276 Some((event, time)) => {
277 return Some(Event {
278 id: idx,
279 event,
280 time,
281 });
282 }
283 None => {
284 self.to_check.pop_front();
285 continue;
286 }
287 };
288 }
289
290 None
291 }
292
293 pub fn gamepad(&self, id: usize) -> Option<&Gamepad> {
294 self.gamepads.get(id)
295 }
296
297 pub fn last_gamepad_hint(&self) -> usize {
298 self.gamepads.len()
299 }
300
301 fn handle_hotplug(&mut self) -> Option<Event> {
302 while let Ok(event) = self.hotplug_rx.try_recv() {
303 match event {
304 HotplugEvent::New { devpath, syspath } => {
305 let gamepad_path_str = devpath.clone().to_string_lossy().into_owned();
307 if self
308 .gamepads
309 .iter()
310 .any(|gamepad| gamepad.devpath == gamepad_path_str && gamepad.is_connected)
311 {
312 continue;
313 }
314 if let Some(gamepad) = Gamepad::open(&devpath, &syspath, self.discovery_backend)
315 {
316 return if let Some(id) = self
317 .gamepads
318 .iter()
319 .position(|gp| gp.uuid() == gamepad.uuid && !gp.is_connected)
320 {
321 if let Err(e) = gamepad.register_fd(&self.epoll, id as u64) {
322 error!("Failed to add gamepad to epoll: {}", e);
323 }
324 self.gamepads[id] = gamepad;
325 Some(Event::new(id, EventType::Connected))
326 } else {
327 if let Err(e) =
328 gamepad.register_fd(&self.epoll, self.gamepads.len() as u64)
329 {
330 error!("Failed to add gamepad to epoll: {}", e);
331 }
332 self.gamepads.push(gamepad);
333 Some(Event::new(self.gamepads.len() - 1, EventType::Connected))
334 };
335 }
336 }
337 HotplugEvent::Removed(devpath) => {
338 if let Some(id) = self
339 .gamepads
340 .iter()
341 .position(|gp| devpath == gp.devpath && gp.is_connected)
342 {
343 let gamepad_fd = unsafe { BorrowedFd::borrow_raw(self.gamepads[id].fd) };
344 if let Err(e) = self.epoll.delete(gamepad_fd) {
345 error!("Failed to remove disconnected gamepad from epoll: {}", e);
346 }
347
348 self.gamepads[id].disconnect();
349 return Some(Event::new(id, EventType::Disconnected));
350 } else {
351 debug!("Could not find disconnected gamepad {devpath:?}");
352 }
353 }
354 }
355 }
356
357 None
358 }
359}
360
361enum HotplugEvent {
362 New { devpath: CString, syspath: PathBuf },
363 Removed(String),
364}
365
366fn handle_inotify(
367 sender: &Sender<HotplugEvent>,
368 event: inotify::Event<&std::ffi::OsStr>,
369 event_fd: &mut EventFd,
370) -> bool {
371 let name = match event.name.and_then(|name| name.to_str()) {
372 Some(name) => name,
373 None => return true,
374 };
375 let (gamepad_path, syspath) = match get_gamepad_path(name) {
376 Some((gamepad_path, syspath)) => (gamepad_path, syspath),
377 None => return true,
378 };
379
380 let mut sent = false;
381
382 if !(event.mask & (EventMask::CREATE | EventMask::MOVED_TO | EventMask::ATTRIB)).is_empty() {
383 if sender
384 .send(HotplugEvent::New {
385 devpath: CString::new(gamepad_path.to_str().unwrap()).unwrap(),
386 syspath,
387 })
388 .is_err()
389 {
390 debug!("All receivers dropped, ending hot plug loop.");
391 return false;
392 }
393 sent = true;
394 } else if !(event.mask & (EventMask::DELETE | EventMask::MOVED_FROM)).is_empty() {
395 if sender
396 .send(HotplugEvent::Removed(
397 gamepad_path.to_string_lossy().to_string(),
398 ))
399 .is_err()
400 {
401 debug!("All receivers dropped, ending hot plug loop.");
402 return false;
403 }
404 sent = true;
405 }
406 if sent {
407 if let Err(e) = event_fd.write(0u64) {
408 error!(
409 "Failed to notify other thread about new hotplug events: {}",
410 e
411 );
412 }
413 }
414 true
415}
416
417fn get_gamepad_path(name: &str) -> Option<(PathBuf, PathBuf)> {
418 let event_id = name.strip_prefix("event")?;
419
420 if event_id.is_empty()
421 || event_id
422 .chars()
423 .any(|character| !character.is_ascii_digit())
424 {
425 return None;
426 }
427
428 let gamepad_path = Path::new(INPUT_DIR_PATH).join(name);
429 let syspath = Path::new("/sys/class/input/").join(name);
430 Some((gamepad_path, syspath))
431}
432
433fn handle_hotplug(sender: Sender<HotplugEvent>, monitor: Monitor, event: EventFd) {
434 loop {
435 if !monitor.wait_hotplug_available() {
436 continue;
437 }
438
439 let dev = monitor.device();
440
441 unsafe {
442 if let Some(val) = dev.property_value(cstr_new(b"ID_INPUT_JOYSTICK\0")) {
443 if val != cstr_new(b"1\0") {
444 continue;
445 }
446 } else {
447 continue;
448 }
449
450 let action = match dev.action() {
451 Some(a) => a,
452 None => continue,
453 };
454
455 let mut sent = false;
456
457 if action == cstr_new(b"add\0") {
458 if let Some((devpath, syspath)) = dev.devnode().zip(dev.syspath()) {
459 let syspath = Path::new(OsStr::from_bytes(syspath.to_bytes()));
460 if sender
461 .send(HotplugEvent::New {
462 devpath: devpath.into(),
463 syspath: syspath.to_path_buf(),
464 })
465 .is_err()
466 {
467 debug!("All receivers dropped, ending hot plug loop.");
468 break;
469 }
470 sent = true;
471 }
472 } else if action == cstr_new(b"remove\0") {
473 if let Some(devnode) = dev.devnode() {
474 if let Ok(str) = devnode.to_str() {
475 if sender.send(HotplugEvent::Removed(str.to_owned())).is_err() {
476 debug!("All receivers dropped, ending hot plug loop.");
477 break;
478 }
479 sent = true;
480 } else {
481 warn!("Received event with devnode that is not valid utf8: {devnode:?}")
482 }
483 }
484 }
485
486 if sent {
487 if let Err(e) = event.write(0) {
488 error!(
489 "Failed to notify other thread about new hotplug events: {}",
490 e
491 );
492 }
493 }
494 }
495 }
496}
497
498#[derive(Debug, Clone)]
499struct AxesInfo {
500 info: VecMap<AxisInfo>,
501}
502
503impl AxesInfo {
504 fn new(fd: i32) -> Self {
505 let mut map = VecMap::new();
506
507 unsafe {
508 let mut abs_bits = [0u8; (ABS_MAX / 8) as usize + 1];
509 ioctl::eviocgbit(
510 fd,
511 u32::from(EV_ABS),
512 abs_bits.len() as i32,
513 abs_bits.as_mut_ptr(),
514 );
515
516 for axis in Gamepad::find_axes(&abs_bits) {
517 let mut info = input_absinfo::default();
518 ioctl::eviocgabs(fd, u32::from(axis.code), &mut info);
519 map.insert(
520 axis.code as usize,
521 AxisInfo {
522 min: info.minimum,
523 max: info.maximum,
524 deadzone: Some(info.flat as u32),
525 },
526 );
527 }
528 }
529
530 AxesInfo { info: map }
531 }
532}
533
534impl Index<u16> for AxesInfo {
535 type Output = AxisInfo;
536
537 fn index(&self, i: u16) -> &Self::Output {
538 &self.info[i as usize]
539 }
540}
541
542#[derive(Debug)]
543pub struct Gamepad {
544 fd: RawFd,
545 axes_info: AxesInfo,
546 ff_supported: bool,
547 devpath: String,
548 name: String,
549 uuid: Uuid,
550 vendor_id: u16,
551 product_id: u16,
552 bt_capacity_fd: RawFd,
553 bt_status_fd: RawFd,
554 axes_values: VecMap<i32>,
555 buttons_values: VecMap<bool>,
556 events: Vec<input_event>,
557 axes: Vec<EvCode>,
558 buttons: Vec<EvCode>,
559 is_connected: bool,
560}
561
562impl Gamepad {
563 fn open(path: &CStr, syspath: &Path, discovery_backend: DiscoveryBackend) -> Option<Gamepad> {
564 if unsafe { !c::strstr(path.as_ptr(), c"js".as_ptr() as *const c_char).is_null() } {
565 trace!("Device {:?} is js interface, ignoring.", path);
566 return None;
567 }
568
569 let fd = unsafe { c::open(path.as_ptr(), c::O_RDWR | c::O_NONBLOCK) };
570 if fd < 0 {
571 log!(
572 match discovery_backend {
573 DiscoveryBackend::Inotify => log::Level::Debug,
574 _ => log::Level::Error,
575 },
576 "Failed to open {:?}",
577 path
578 );
579 return None;
580 }
581
582 let input_id = match Self::get_input_id(fd) {
583 Some(input_id) => input_id,
584 None => {
585 error!("Failed to get id of device {:?}", path);
586 unsafe {
587 c::close(fd);
588 }
589 return None;
590 }
591 };
592
593 let name = Self::get_name(fd).unwrap_or_else(|| {
594 error!("Failed to get name of device {:?}", path);
595 "Unknown".into()
596 });
597
598 let axesi = AxesInfo::new(fd);
599 let ff_supported = Self::test_ff(fd);
600 let (cap, status) = Self::battery_fd(syspath);
601
602 let mut gamepad = Gamepad {
603 fd,
604 axes_info: axesi,
605 ff_supported,
606 devpath: path.to_string_lossy().into_owned(),
607 name,
608 uuid: create_uuid(input_id),
609 vendor_id: input_id.vendor,
610 product_id: input_id.product,
611 bt_capacity_fd: cap,
612 bt_status_fd: status,
613 axes_values: VecMap::new(),
614 buttons_values: VecMap::new(),
615 events: Vec::new(),
616 axes: Vec::new(),
617 buttons: Vec::new(),
618 is_connected: true,
619 };
620
621 gamepad.collect_axes_and_buttons();
622
623 if !gamepad.is_gamepad() {
624 log!(
625 match discovery_backend {
626 DiscoveryBackend::Inotify => log::Level::Debug,
627 _ => log::Level::Warn,
628 },
629 "{:?} doesn't have at least 1 button and 2 axes, ignoring.",
630 path
631 );
632 return None;
633 }
634
635 info!("Gamepad {} ({}) connected.", gamepad.devpath, gamepad.name);
636 debug!(
637 "Gamepad {}: uuid: {}, ff_supported: {}, axes: {:?}, buttons: {:?}, axes_info: {:?}",
638 gamepad.devpath,
639 gamepad.uuid,
640 gamepad.ff_supported,
641 gamepad.axes,
642 gamepad.buttons,
643 gamepad.axes_info
644 );
645
646 Some(gamepad)
647 }
648
649 fn register_fd(&self, epoll: &Epoll, data: u64) -> Result<(), Errno> {
650 let fd = unsafe { BorrowedFd::borrow_raw(self.fd) };
651 epoll.add(fd, EpollEvent::new(EpollFlags::EPOLLIN, data))
652 }
653
654 fn collect_axes_and_buttons(&mut self) {
655 let mut key_bits = [0u8; (KEY_MAX / 8) as usize + 1];
656 let mut abs_bits = [0u8; (ABS_MAX / 8) as usize + 1];
657
658 unsafe {
659 ioctl::eviocgbit(
660 self.fd,
661 u32::from(EV_KEY),
662 key_bits.len() as i32,
663 key_bits.as_mut_ptr(),
664 );
665 ioctl::eviocgbit(
666 self.fd,
667 u32::from(EV_ABS),
668 abs_bits.len() as i32,
669 abs_bits.as_mut_ptr(),
670 );
671 }
672
673 self.buttons = Self::find_buttons(&key_bits, false);
674 self.axes = Self::find_axes(&abs_bits);
675 }
676
677 fn get_name(fd: i32) -> Option<String> {
678 unsafe {
679 let mut namebuff: [MaybeUninit<u8>; 128] = MaybeUninit::uninit().assume_init();
680 if ioctl::eviocgname(fd, &mut namebuff).is_err() {
681 None
682 } else {
683 Some(
684 CStr::from_ptr(namebuff.as_ptr() as *const c_char)
685 .to_string_lossy()
686 .into_owned(),
687 )
688 }
689 }
690 }
691
692 fn get_input_id(fd: i32) -> Option<ioctl::input_id> {
693 unsafe {
694 let mut iid = MaybeUninit::<ioctl::input_id>::uninit();
695 if ioctl::eviocgid(fd, iid.as_mut_ptr()).is_err() {
696 return None;
697 }
698
699 Some(iid.assume_init())
700 }
701 }
702
703 fn test_ff(fd: i32) -> bool {
704 unsafe {
705 let mut ff_bits = [0u8; (FF_MAX / 8) as usize + 1];
706 if ioctl::eviocgbit(
707 fd,
708 u32::from(EV_FF),
709 ff_bits.len() as i32,
710 ff_bits.as_mut_ptr(),
711 ) >= 0
712 {
713 utils::test_bit(FF_SQUARE, &ff_bits)
714 && utils::test_bit(FF_TRIANGLE, &ff_bits)
715 && utils::test_bit(FF_SINE, &ff_bits)
716 && utils::test_bit(FF_GAIN, &ff_bits)
717 } else {
718 false
719 }
720 }
721 }
722
723 fn is_gamepad(&self) -> bool {
724 !self.buttons.is_empty() && self.axes.len() >= 2
726 }
727
728 fn find_buttons(key_bits: &[u8], only_gamepad_btns: bool) -> Vec<EvCode> {
729 let mut buttons = Vec::with_capacity(16);
730
731 for bit in BTN_MISC..BTN_MOUSE {
732 if utils::test_bit(bit, key_bits) {
733 buttons.push(EvCode::new(EV_KEY, bit));
734 }
735 }
736 for bit in BTN_JOYSTICK..(key_bits.len() as u16 * 8) {
737 if utils::test_bit(bit, key_bits) {
738 buttons.push(EvCode::new(EV_KEY, bit));
739 }
740 }
741
742 if !only_gamepad_btns {
743 for bit in 0..BTN_MISC {
744 if utils::test_bit(bit, key_bits) {
745 buttons.push(EvCode::new(EV_KEY, bit));
746 }
747 }
748 for bit in BTN_MOUSE..BTN_JOYSTICK {
749 if utils::test_bit(bit, key_bits) {
750 buttons.push(EvCode::new(EV_KEY, bit));
751 }
752 }
753 }
754
755 buttons
756 }
757
758 fn find_axes(abs_bits: &[u8]) -> Vec<EvCode> {
759 let mut axes = Vec::with_capacity(8);
760
761 for bit in 0..(abs_bits.len() * 8) {
762 if utils::test_bit(bit as u16, abs_bits) {
763 axes.push(EvCode::new(EV_ABS, bit as u16));
764 }
765 }
766
767 axes
768 }
769
770 fn battery_fd(syspath: &Path) -> (i32, i32) {
771 use std::fs::{self};
772 use std::os::unix::io::IntoRawFd;
773
774 let syspath = syspath.join("device/device/power_supply");
777 if let Ok(mut read_dir) = fs::read_dir(syspath) {
778 if let Some(Ok(bat_entry)) = read_dir.next() {
779 if let Ok(cap) = File::open(bat_entry.path().join("capacity")) {
780 if let Ok(status) = File::open(bat_entry.path().join("status")) {
781 return (cap.into_raw_fd(), status.into_raw_fd());
782 }
783 }
784 }
785 }
786 (-1, -1)
787 }
788
789 fn event(&mut self) -> Option<(EventType, SystemTime)> {
790 let mut skip = false;
791 loop {
794 let event = self.next_event()?;
795
796 if skip {
797 if event.type_ == EV_SYN && event.code == SYN_REPORT {
798 skip = false;
799 self.compare_state();
800 }
801 continue;
802 }
803
804 let ev = match event.type_ {
805 EV_SYN if event.code == SYN_DROPPED => {
806 skip = true;
807 None
808 }
809 EV_KEY => {
810 self.buttons_values
811 .insert(event.code as usize, event.value == 1);
812 match event.value {
813 0 => Some(EventType::ButtonReleased(event.into())),
814 1 => Some(EventType::ButtonPressed(event.into())),
815 _ => None,
816 }
817 }
818 EV_ABS => {
819 self.axes_values.insert(event.code as usize, event.value);
820 Some(EventType::AxisValueChanged(event.value, event.into()))
821 }
822 _ => {
823 trace!("Skipping event {:?}", event);
824 None
825 }
826 };
827
828 if let Some(ev) = ev {
829 let dur = Duration::new(event.time.tv_sec as u64, event.time.tv_usec as u32 * 1000);
830
831 return Some((ev, UNIX_EPOCH + dur));
832 }
833 }
834 }
835
836 fn next_event(&mut self) -> Option<input_event> {
837 if !self.events.is_empty() {
838 self.events.pop()
839 } else {
840 unsafe {
841 let mut event_buf: [MaybeUninit<ioctl::input_event>; 12] =
842 MaybeUninit::uninit().assume_init();
843 let size = mem::size_of::<ioctl::input_event>();
844 let n = c::read(
845 self.fd,
846 event_buf.as_mut_ptr() as *mut c::c_void,
847 size * event_buf.len(),
848 );
849
850 if n == -1 || n == 0 {
851 None
853 } else if n % size as isize != 0 {
854 error!("Unexpected read of size {}", n);
855 None
856 } else {
857 let n = n as usize / size;
858 trace!("Got {} new events", n);
859 for ev in event_buf[1..n].iter().rev() {
860 self.events.push(ev.assume_init());
861 }
862
863 Some(event_buf[0].assume_init())
864 }
865 }
866 }
867 }
868
869 fn compare_state(&mut self) {
870 let mut absinfo = input_absinfo::default();
871 for axis in self.axes.iter().cloned() {
872 let value = unsafe {
873 ioctl::eviocgabs(self.fd, u32::from(axis.code), &mut absinfo);
874 absinfo.value
875 };
876
877 if self
878 .axes_values
879 .get(axis.code as usize)
880 .cloned()
881 .unwrap_or(0)
882 != value
883 {
884 self.events.push(input_event {
885 type_: EV_ABS,
886 code: axis.code,
887 value,
888 ..Default::default()
889 });
890 }
891 }
892
893 let mut buf = [0u8; KEY_MAX as usize / 8 + 1];
894 unsafe {
895 let _ = ioctl::eviocgkey(self.fd, &mut buf);
896 }
897
898 for btn in self.buttons.iter().cloned() {
899 let val = utils::test_bit(btn.code, &buf);
900 if self
901 .buttons_values
902 .get(btn.code as usize)
903 .cloned()
904 .unwrap_or(false)
905 != val
906 {
907 self.events.push(input_event {
908 type_: EV_KEY,
909 code: btn.code,
910 value: val as i32,
911 ..Default::default()
912 });
913 }
914 }
915 }
916
917 fn disconnect(&mut self) {
918 unsafe {
919 if self.fd >= 0 {
920 c::close(self.fd);
921 }
922 }
923 self.fd = -2;
924 self.devpath.clear();
925 self.is_connected = false;
926 }
927
928 pub fn is_connected(&self) -> bool {
929 self.is_connected
930 }
931
932 pub fn power_info(&self) -> PowerInfo {
933 if self.bt_capacity_fd > -1 && self.bt_status_fd > -1 {
934 unsafe {
935 let mut buff = [0u8; 15];
936 c::lseek(self.bt_capacity_fd, 0, c::SEEK_SET);
937 c::lseek(self.bt_status_fd, 0, c::SEEK_SET);
938
939 let len = c::read(
940 self.bt_capacity_fd,
941 buff.as_mut_ptr() as *mut c::c_void,
942 buff.len(),
943 );
944
945 if len > 0 {
946 let len = len as usize;
947 let cap = match str::from_utf8_unchecked(&buff[..(len - 1)]).parse() {
948 Ok(cap) => cap,
949 Err(_) => {
950 error!(
951 "Failed to parse battery capacity: {}",
952 str::from_utf8_unchecked(&buff[..(len - 1)])
953 );
954 return PowerInfo::Unknown;
955 }
956 };
957
958 let len = c::read(
959 self.bt_status_fd,
960 buff.as_mut_ptr() as *mut c::c_void,
961 buff.len(),
962 );
963
964 if len > 0 {
965 let len = len as usize;
966 return match str::from_utf8_unchecked(&buff[..(len - 1)]) {
967 "Charging" => PowerInfo::Charging(cap),
968 "Discharging" => PowerInfo::Discharging(cap),
969 "Full" | "Not charging" => PowerInfo::Charged,
970 s => {
971 error!("Unknown battery status value: {}", s);
972 PowerInfo::Unknown
973 }
974 };
975 }
976 }
977 }
978 PowerInfo::Unknown
979 } else if self.fd > -1 {
980 PowerInfo::Wired
981 } else {
982 PowerInfo::Unknown
983 }
984 }
985
986 pub fn is_ff_supported(&self) -> bool {
987 self.ff_supported
988 }
989
990 pub fn name(&self) -> &str {
991 &self.name
992 }
993
994 pub fn devpath(&self) -> &str {
995 &self.devpath
996 }
997
998 pub fn uuid(&self) -> Uuid {
999 self.uuid
1000 }
1001
1002 pub fn vendor_id(&self) -> Option<u16> {
1003 Some(self.vendor_id)
1004 }
1005
1006 pub fn product_id(&self) -> Option<u16> {
1007 Some(self.product_id)
1008 }
1009
1010 pub fn ff_device(&self) -> Option<FfDevice> {
1011 if self.is_ff_supported() {
1012 FfDevice::new(&self.devpath).ok()
1013 } else {
1014 None
1015 }
1016 }
1017
1018 pub fn buttons(&self) -> &[EvCode] {
1019 &self.buttons
1020 }
1021
1022 pub fn axes(&self) -> &[EvCode] {
1023 &self.axes
1024 }
1025
1026 pub(crate) fn axis_info(&self, nec: EvCode) -> Option<&AxisInfo> {
1027 if nec.kind != EV_ABS {
1028 None
1029 } else {
1030 self.axes_info.info.get(nec.code as usize)
1031 }
1032 }
1033}
1034
1035impl Drop for Gamepad {
1036 fn drop(&mut self) {
1037 unsafe {
1038 if self.fd >= 0 {
1039 c::close(self.fd);
1040 }
1041 if self.bt_capacity_fd >= 0 {
1042 c::close(self.bt_capacity_fd);
1043 }
1044 if self.bt_status_fd >= 0 {
1045 c::close(self.bt_status_fd);
1046 }
1047 }
1048 }
1049}
1050
1051impl PartialEq for Gamepad {
1052 fn eq(&self, other: &Self) -> bool {
1053 self.uuid == other.uuid
1054 }
1055}
1056
1057fn create_uuid(iid: ioctl::input_id) -> Uuid {
1058 let bus = (u32::from(iid.bustype)).to_be();
1059 let vendor = iid.vendor.to_be();
1060 let product = iid.product.to_be();
1061 let version = iid.version.to_be();
1062 Uuid::from_fields(
1063 bus,
1064 vendor,
1065 0,
1066 &[
1067 (product >> 8) as u8,
1068 product as u8,
1069 0,
1070 0,
1071 (version >> 8) as u8,
1072 version as u8,
1073 0,
1074 0,
1075 ],
1076 )
1077}
1078
1079unsafe fn cstr_new(bytes: &[u8]) -> &CStr {
1080 CStr::from_bytes_with_nul_unchecked(bytes)
1081}
1082
1083#[cfg(feature = "serde-serialize")]
1084use serde::{Deserialize, Serialize};
1085
1086#[derive(Copy, Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Hash)]
1087#[cfg_attr(feature = "serde-serialize", derive(Serialize, Deserialize))]
1088pub struct EvCode {
1089 kind: u16,
1090 code: u16,
1091}
1092
1093impl EvCode {
1094 fn new(kind: u16, code: u16) -> Self {
1095 EvCode { kind, code }
1096 }
1097
1098 pub fn into_u32(self) -> u32 {
1099 (u32::from(self.kind) << 16) | u32::from(self.code)
1100 }
1101}
1102
1103impl From<input_event> for crate::EvCode {
1104 fn from(f: input_event) -> Self {
1105 crate::EvCode(EvCode {
1106 kind: f.type_,
1107 code: f.code,
1108 })
1109 }
1110}
1111
1112impl Display for EvCode {
1113 fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
1114 match self.kind {
1115 EV_SYN => f.write_str("SYN")?,
1116 EV_KEY => f.write_str("KEY")?,
1117 EV_REL => f.write_str("REL")?,
1118 EV_ABS => f.write_str("ABS")?,
1119 EV_MSC => f.write_str("MSC")?,
1120 EV_SW => f.write_str("SW")?,
1121 kind => f.write_fmt(format_args!("EV_TYPE_{}", kind))?,
1122 }
1123
1124 f.write_fmt(format_args!("({})", self.code))
1125 }
1126}
1127
1128#[derive(Debug, Copy, Clone)]
1129#[allow(clippy::enum_variant_names)]
1130enum Error {
1131 UdevCtx,
1132 UdevEnumerate,
1133 NullSyspath,
1134 Errno(Errno, &'static str),
1135}
1136
1137impl Display for Error {
1138 fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
1139 match *self {
1140 Error::UdevCtx => f.write_str("Failed to create udev context"),
1141 Error::UdevEnumerate => f.write_str("Failed to create udev enumerate object"),
1142 Error::NullSyspath => f.write_str("Returned syspath was null"),
1143 Error::Errno(e, ctx) => f.write_fmt(format_args!("{} failed: {}", ctx, e)),
1144 }
1145 }
1146}
1147
1148impl error::Error for Error {}
1149
1150fn errno_to_platform_error(errno: Errno, ctx: &'static str) -> PlatformError {
1151 PlatformError::Other(Box::new(Error::Errno(errno, ctx)))
1152}
1153
1154const KEY_MAX: u16 = 0x2ff;
1155#[allow(dead_code)]
1156const EV_MAX: u16 = 0x1f;
1157const EV_SYN: u16 = 0x00;
1158const EV_KEY: u16 = 0x01;
1159const EV_REL: u16 = 0x02;
1160const EV_ABS: u16 = 0x03;
1161const EV_MSC: u16 = 0x04;
1162const EV_SW: u16 = 0x05;
1163const ABS_MAX: u16 = 0x3f;
1164const EV_FF: u16 = 0x15;
1165
1166const SYN_REPORT: u16 = 0x00;
1167const SYN_DROPPED: u16 = 0x03;
1168
1169const BTN_MISC: u16 = 0x100;
1170const BTN_MOUSE: u16 = 0x110;
1171const BTN_JOYSTICK: u16 = 0x120;
1172const BTN_SOUTH: u16 = 0x130;
1173const BTN_EAST: u16 = 0x131;
1174#[allow(dead_code)]
1175const BTN_C: u16 = 0x132;
1176const BTN_NORTH: u16 = 0x133;
1177const BTN_WEST: u16 = 0x134;
1178#[allow(dead_code)]
1179const BTN_Z: u16 = 0x135;
1180const BTN_TL: u16 = 0x136;
1181const BTN_TR: u16 = 0x137;
1182const BTN_TL2: u16 = 0x138;
1183const BTN_TR2: u16 = 0x139;
1184const BTN_SELECT: u16 = 0x13a;
1185const BTN_START: u16 = 0x13b;
1186const BTN_MODE: u16 = 0x13c;
1187const BTN_THUMBL: u16 = 0x13d;
1188const BTN_THUMBR: u16 = 0x13e;
1189
1190const BTN_DPAD_UP: u16 = 0x220;
1191const BTN_DPAD_DOWN: u16 = 0x221;
1192const BTN_DPAD_LEFT: u16 = 0x222;
1193const BTN_DPAD_RIGHT: u16 = 0x223;
1194
1195const ABS_X: u16 = 0x00;
1196const ABS_Y: u16 = 0x01;
1197const ABS_Z: u16 = 0x02;
1198const ABS_RX: u16 = 0x03;
1199const ABS_RY: u16 = 0x04;
1200const ABS_RZ: u16 = 0x05;
1201const ABS_HAT0X: u16 = 0x10;
1202const ABS_HAT0Y: u16 = 0x11;
1203const ABS_HAT1X: u16 = 0x12;
1204const ABS_HAT1Y: u16 = 0x13;
1205const ABS_HAT2X: u16 = 0x14;
1206const ABS_HAT2Y: u16 = 0x15;
1207
1208const FF_MAX: u16 = FF_GAIN;
1209const FF_SQUARE: u16 = 0x58;
1210const FF_TRIANGLE: u16 = 0x59;
1211const FF_SINE: u16 = 0x5a;
1212const FF_GAIN: u16 = 0x60;
1213
1214pub mod native_ev_codes {
1215 use super::*;
1216
1217 pub const BTN_SOUTH: EvCode = EvCode {
1218 kind: EV_KEY,
1219 code: super::BTN_SOUTH,
1220 };
1221 pub const BTN_EAST: EvCode = EvCode {
1222 kind: EV_KEY,
1223 code: super::BTN_EAST,
1224 };
1225 pub const BTN_C: EvCode = EvCode {
1226 kind: EV_KEY,
1227 code: super::BTN_C,
1228 };
1229 pub const BTN_NORTH: EvCode = EvCode {
1230 kind: EV_KEY,
1231 code: super::BTN_NORTH,
1232 };
1233 pub const BTN_WEST: EvCode = EvCode {
1234 kind: EV_KEY,
1235 code: super::BTN_WEST,
1236 };
1237 pub const BTN_Z: EvCode = EvCode {
1238 kind: EV_KEY,
1239 code: super::BTN_Z,
1240 };
1241 pub const BTN_LT: EvCode = EvCode {
1242 kind: EV_KEY,
1243 code: super::BTN_TL,
1244 };
1245 pub const BTN_RT: EvCode = EvCode {
1246 kind: EV_KEY,
1247 code: super::BTN_TR,
1248 };
1249 pub const BTN_LT2: EvCode = EvCode {
1250 kind: EV_KEY,
1251 code: super::BTN_TL2,
1252 };
1253 pub const BTN_RT2: EvCode = EvCode {
1254 kind: EV_KEY,
1255 code: super::BTN_TR2,
1256 };
1257 pub const BTN_SELECT: EvCode = EvCode {
1258 kind: EV_KEY,
1259 code: super::BTN_SELECT,
1260 };
1261 pub const BTN_START: EvCode = EvCode {
1262 kind: EV_KEY,
1263 code: super::BTN_START,
1264 };
1265 pub const BTN_MODE: EvCode = EvCode {
1266 kind: EV_KEY,
1267 code: super::BTN_MODE,
1268 };
1269 pub const BTN_LTHUMB: EvCode = EvCode {
1270 kind: EV_KEY,
1271 code: super::BTN_THUMBL,
1272 };
1273 pub const BTN_RTHUMB: EvCode = EvCode {
1274 kind: EV_KEY,
1275 code: super::BTN_THUMBR,
1276 };
1277 pub const BTN_DPAD_UP: EvCode = EvCode {
1278 kind: EV_KEY,
1279 code: super::BTN_DPAD_UP,
1280 };
1281 pub const BTN_DPAD_DOWN: EvCode = EvCode {
1282 kind: EV_KEY,
1283 code: super::BTN_DPAD_DOWN,
1284 };
1285 pub const BTN_DPAD_LEFT: EvCode = EvCode {
1286 kind: EV_KEY,
1287 code: super::BTN_DPAD_LEFT,
1288 };
1289 pub const BTN_DPAD_RIGHT: EvCode = EvCode {
1290 kind: EV_KEY,
1291 code: super::BTN_DPAD_RIGHT,
1292 };
1293
1294 pub const AXIS_LSTICKX: EvCode = EvCode {
1295 kind: EV_ABS,
1296 code: super::ABS_X,
1297 };
1298 pub const AXIS_LSTICKY: EvCode = EvCode {
1299 kind: EV_ABS,
1300 code: super::ABS_Y,
1301 };
1302 pub const AXIS_LEFTZ: EvCode = EvCode {
1303 kind: EV_ABS,
1304 code: super::ABS_Z,
1305 };
1306 pub const AXIS_RSTICKX: EvCode = EvCode {
1307 kind: EV_ABS,
1308 code: super::ABS_RX,
1309 };
1310 pub const AXIS_RSTICKY: EvCode = EvCode {
1311 kind: EV_ABS,
1312 code: super::ABS_RY,
1313 };
1314 pub const AXIS_RIGHTZ: EvCode = EvCode {
1315 kind: EV_ABS,
1316 code: super::ABS_RZ,
1317 };
1318 pub const AXIS_DPADX: EvCode = EvCode {
1319 kind: EV_ABS,
1320 code: super::ABS_HAT0X,
1321 };
1322 pub const AXIS_DPADY: EvCode = EvCode {
1323 kind: EV_ABS,
1324 code: super::ABS_HAT0Y,
1325 };
1326 pub const AXIS_RT: EvCode = EvCode {
1327 kind: EV_ABS,
1328 code: super::ABS_HAT1X,
1329 };
1330 pub const AXIS_LT: EvCode = EvCode {
1331 kind: EV_ABS,
1332 code: super::ABS_HAT1Y,
1333 };
1334 pub const AXIS_RT2: EvCode = EvCode {
1335 kind: EV_ABS,
1336 code: super::ABS_HAT2X,
1337 };
1338 pub const AXIS_LT2: EvCode = EvCode {
1339 kind: EV_ABS,
1340 code: super::ABS_HAT2Y,
1341 };
1342}
1343
1344#[cfg(test)]
1345mod tests {
1346 use super::super::ioctl;
1347 use super::create_uuid;
1348 use uuid::Uuid;
1349
1350 #[test]
1351 fn sdl_uuid() {
1352 let x = Uuid::parse_str("030000005e0400008e02000020200000").unwrap();
1353 let y = create_uuid(ioctl::input_id {
1354 bustype: 0x3,
1355 vendor: 0x045e,
1356 product: 0x028e,
1357 version: 0x2020,
1358 });
1359 assert_eq!(x, y);
1360 }
1361}