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(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                    // We already know this gamepad, ignore it:
302                    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        // TODO: improve it (for example check for buttons in range)
721        !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        // Returned syspath points to <device path>/input/inputXX/eventXX. First "device" is
771        // symlink to inputXX, second to actual device root.
772        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        // Skip all unknown events and return Option on first know event or when there is no more
788        // events to read. Returning None on unknown event breaks iterators.
789        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                    // Nothing to read (non-blocking IO)
848                    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}