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