ferroui_linuxframebuffer/input/ev_dev/
ev_dev_device.rs1use crate::native_unsafe_methods::{abs_axis, input_absinfo};
9use std::collections::HashMap;
10
11#[derive(Clone, Debug, Default, PartialEq)]
14pub struct EvDevDeviceInfo {
15 pub name: Option<String>,
16 pub event_types: Vec<u16>,
18 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#[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 const fn eviocgname(len: usize) -> u32 {
53 ioc(IOC_READ, b'E', 0x06, len)
54 }
55
56 const fn eviocgbit(ev: u32, len: usize) -> u32 {
58 ioc(IOC_READ, b'E', 0x20 + ev, len)
59 }
60
61 const fn eviocgabs(abs: u32) -> u32 {
63 ioc(IOC_READ, b'E', 0x40 + abs, std::mem::size_of::<input_absinfo>())
64 }
65
66 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 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 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 let fd = unsafe { OwnedFd::from_raw_fd(fd) };
113 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 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 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 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 pub fn read_events(fd: RawFd) -> Vec<input_event> {
156 let mut rv = Vec::new();
157 loop {
158 let mut buffer: [libc::input_event; 64] = unsafe { std::mem::zeroed() };
160 let size = std::mem::size_of::<libc::input_event>();
161 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 use super::*;
185
186 #[test]
187 fn the_request_numbers_are_those_of_the_kernel() {
188 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}