Skip to main content

ferroui_linuxframebuffer/input/ev_dev/
ev_dev_device.rs

1//! An event device of the kernel (the port of `EvDevDevice.cs`).
2//!
3//! The reference asks the `libevdev` library for the name, the event
4//! types, the absolute axes and the next event. The same facts are read
5//! from the device itself here (docs/porting/linux-framebuffer-platform.md,
6//! section 3): `EVIOCGNAME`, `EVIOCGBIT`, `EVIOCGABS` and `read`.
7
8use crate::native_unsafe_methods::{abs_axis, input_absinfo};
9use std::collections::HashMap;
10
11/// What is known of a device once it is open: the part of `EvDevDevice`
12/// that is values.
13#[derive(Clone, Debug, Default, PartialEq)]
14pub struct EvDevDeviceInfo {
15    pub name: Option<String>,
16    /// The event types the device reports (`ev_type`).
17    pub event_types: Vec<u16>,
18    /// The absolute axes the device has, by code. An addition: the
19    /// reference reads the two below only.
20    pub abs: HashMap<u16, input_absinfo>,
21}
22
23impl EvDevDeviceInfo {
24    pub fn abs_x(&self) -> Option<input_absinfo> {
25        self.abs.get(&abs_axis::ABS_X).copied()
26    }
27
28    pub fn abs_y(&self) -> Option<input_absinfo> {
29        self.abs.get(&abs_axis::ABS_Y).copied()
30    }
31}
32
33/// The range of an axis.
34#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
35pub struct EvDevAxisInfo {
36    pub minimum: i32,
37    pub maximum: i32,
38}
39
40#[cfg(target_os = "linux")]
41pub use imp::EvDevDevice;
42
43#[cfg(target_os = "linux")]
44mod imp {
45    use super::EvDevDeviceInfo;
46    use crate::native_unsafe_methods::{abs_axis, ev_type, input_absinfo, input_event, ioc, sys, IOC_READ};
47    use std::collections::HashMap;
48    use std::io;
49    use std::os::fd::{AsRawFd, FromRawFd, OwnedFd, RawFd};
50
51    /// `EVIOCGNAME(len)`.
52    const fn eviocgname(len: usize) -> u32 {
53        ioc(IOC_READ, b'E', 0x06, len)
54    }
55
56    /// `EVIOCGBIT(ev, len)`.
57    const fn eviocgbit(ev: u32, len: usize) -> u32 {
58        ioc(IOC_READ, b'E', 0x20 + ev, len)
59    }
60
61    /// `EVIOCGABS(abs)`.
62    const fn eviocgabs(abs: u32) -> u32 {
63        ioc(IOC_READ, b'E', 0x40 + abs, std::mem::size_of::<input_absinfo>())
64    }
65
66    /// The axes that are asked for: those of the reference and those of
67    /// the multi-touch protocol.
68    const AXES: [u16; 5] = [
69        abs_axis::ABS_X,
70        abs_axis::ABS_Y,
71        abs_axis::ABS_MT_SLOT,
72        abs_axis::ABS_MT_POSITION_X,
73        abs_axis::ABS_MT_POSITION_Y,
74    ];
75
76    pub struct EvDevDevice {
77        fd: OwnedFd,
78        info: EvDevDeviceInfo,
79    }
80
81    impl EvDevDevice {
82        pub fn fd(&self) -> RawFd {
83            self.fd.as_raw_fd()
84        }
85
86        pub fn info(&self) -> &EvDevDeviceInfo {
87            &self.info
88        }
89
90        pub fn name(&self) -> Option<&str> {
91            self.info.name.as_deref()
92        }
93
94        /// The event types the device reports (`ev_type`).
95        pub fn event_types(&self) -> &[u16] {
96            &self.info.event_types
97        }
98
99        pub fn abs_x(&self) -> Option<input_absinfo> {
100            self.info.abs_x()
101        }
102
103        pub fn abs_y(&self) -> Option<input_absinfo> {
104            self.info.abs_y()
105        }
106
107        /// Opens the device without blocking and reads what it is.
108        pub fn open(device: &str) -> io::Result<Self> {
109            let fd = sys::open(device, libc::O_RDONLY | libc::O_NONBLOCK)
110                .map_err(|error| io::Error::new(error.kind(), format!("Unable to open {device} code {error}")))?;
111            // SAFETY: `fd` was just opened and is owned by nothing else.
112            let fd = unsafe { OwnedFd::from_raw_fd(fd) };
113            // A failure drops the descriptor, which closes it.
114            Self::new(fd).map_err(|error| {
115                io::Error::new(error.kind(), format!("Unable to initialize evdev for {device} code {error}"))
116            })
117        }
118
119        /// The device open on `fd`: reads its name, the event types it
120        /// reports and its absolute axes, which the reference's constructor
121        /// asks `libevdev` for. Fails when the descriptor is not an event
122        /// device (its name cannot be read).
123        pub fn new(fd: OwnedFd) -> io::Result<Self> {
124            let raw = fd.as_raw_fd();
125
126            let mut name = [0u8; 256];
127            sys::ioctl_buffer(raw, eviocgname(name.len()), &mut name)?;
128            let len = name.iter().position(|&b| b == 0).unwrap_or(name.len());
129            let name = Some(String::from_utf8_lossy(&name[..len]).into_owned());
130
131            // The bits of the event types the device has.
132            let mut bits = [0u8; 4];
133            let _ = sys::ioctl_buffer(raw, eviocgbit(0, bits.len()), &mut bits);
134            let has = |bits: &[u8], bit: u16| bits.get(bit as usize / 8).is_some_and(|b| b & (1 << (bit % 8)) != 0);
135            let event_types = ev_type::ALL.into_iter().filter(|&type_| has(&bits, type_)).collect();
136
137            // The axes it has, and the range of each.
138            let mut abs_bits = [0u8; 8];
139            let _ = sys::ioctl_buffer(raw, eviocgbit(ev_type::EV_ABS as u32, abs_bits.len()), &mut abs_bits);
140            let mut abs = HashMap::new();
141            for code in AXES {
142                if !has(&abs_bits, code) {
143                    continue;
144                }
145                let mut absinfo = input_absinfo::default();
146                if sys::ioctl_struct(raw, eviocgabs(code as u32), &mut absinfo).is_ok() {
147                    abs.insert(code, absinfo);
148                }
149            }
150
151            Ok(Self { fd, info: EvDevDeviceInfo { name, event_types, abs } })
152        }
153
154        /// The events that can be read now, in order.
155        pub fn read_events(fd: RawFd) -> Vec<input_event> {
156            let mut rv = Vec::new();
157            loop {
158                // SAFETY: an all-zero `input_event` is valid.
159                let mut buffer: [libc::input_event; 64] = unsafe { std::mem::zeroed() };
160                let size = std::mem::size_of::<libc::input_event>();
161                // SAFETY: reads at most the size of the local buffer; the
162                // kernel writes whole events.
163                let read = unsafe { libc::read(fd, buffer.as_mut_ptr().cast(), size * buffer.len()) };
164                if read <= 0 {
165                    break;
166                }
167                for ev in &buffer[..read as usize / size] {
168                    rv.push(input_event {
169                        tv_sec: ev.time.tv_sec as i64,
170                        tv_usec: ev.time.tv_usec as i64,
171                        type_: ev.type_,
172                        code: ev.code,
173                        value: ev.value,
174                    });
175                }
176            }
177            rv
178        }
179    }
180
181    #[cfg(test)]
182    mod tests {
183        // Not from the reference.
184        use super::*;
185
186        #[test]
187        fn the_request_numbers_are_those_of_the_kernel() {
188            // From linux/input.h, for a 64-bit and a 32-bit kernel alike.
189            assert_eq!(eviocgname(256), 0x8100_4506);
190            assert_eq!(eviocgbit(0, 4), 0x8004_4520);
191            assert_eq!(eviocgabs(0), 0x8018_4540);
192            assert_eq!(eviocgabs(0x35), 0x8018_4575);
193        }
194    }
195}