gilrs_core/platform/linux/
gamepad.rs

1// Copyright 2016-2018 Mateusz Sieczko and other GilRs Developers
2//
3// Licensed under the Apache License, Version 2.0, <LICENSE-APACHE or
4// http://apache.org/licenses/LICENSE-2.0> or the MIT license <LICENSE-MIT or
5// http://opensource.org/licenses/MIT>, at your option. This file may not be
6// copied, modified, or distributed except according to those terms.
7
8use 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                    // We already know this gamepad, ignore it:
306                    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        // TODO: improve it (for example check for buttons in range)
725        !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        // Returned syspath points to <device path>/input/inputXX/eventXX. First "device" is
775        // symlink to inputXX, second to actual device root.
776        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        // Skip all unknown events and return Option on first know event or when there is no more
792        // events to read. Returning None on unknown event breaks iterators.
793        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                    // Nothing to read (non-blocking IO)
852                    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}