use crate::native_unsafe_methods::{abs_axis, input_absinfo};
use std::collections::HashMap;
#[derive(Clone, Debug, Default, PartialEq)]
pub struct EvDevDeviceInfo {
pub name: Option<String>,
pub event_types: Vec<u16>,
pub abs: HashMap<u16, input_absinfo>,
}
impl EvDevDeviceInfo {
pub fn abs_x(&self) -> Option<input_absinfo> {
self.abs.get(&abs_axis::ABS_X).copied()
}
pub fn abs_y(&self) -> Option<input_absinfo> {
self.abs.get(&abs_axis::ABS_Y).copied()
}
}
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
pub struct EvDevAxisInfo {
pub minimum: i32,
pub maximum: i32,
}
#[cfg(target_os = "linux")]
pub use imp::EvDevDevice;
#[cfg(target_os = "linux")]
mod imp {
use super::EvDevDeviceInfo;
use crate::native_unsafe_methods::{abs_axis, ev_type, input_absinfo, input_event, ioc, sys, IOC_READ};
use std::collections::HashMap;
use std::io;
use std::os::fd::{AsRawFd, FromRawFd, OwnedFd, RawFd};
const fn eviocgname(len: usize) -> u32 {
ioc(IOC_READ, b'E', 0x06, len)
}
const fn eviocgbit(ev: u32, len: usize) -> u32 {
ioc(IOC_READ, b'E', 0x20 + ev, len)
}
const fn eviocgabs(abs: u32) -> u32 {
ioc(IOC_READ, b'E', 0x40 + abs, std::mem::size_of::<input_absinfo>())
}
const AXES: [u16; 5] = [
abs_axis::ABS_X,
abs_axis::ABS_Y,
abs_axis::ABS_MT_SLOT,
abs_axis::ABS_MT_POSITION_X,
abs_axis::ABS_MT_POSITION_Y,
];
pub struct EvDevDevice {
fd: OwnedFd,
info: EvDevDeviceInfo,
}
impl EvDevDevice {
pub fn fd(&self) -> RawFd {
self.fd.as_raw_fd()
}
pub fn info(&self) -> &EvDevDeviceInfo {
&self.info
}
pub fn name(&self) -> Option<&str> {
self.info.name.as_deref()
}
pub fn event_types(&self) -> &[u16] {
&self.info.event_types
}
pub fn abs_x(&self) -> Option<input_absinfo> {
self.info.abs_x()
}
pub fn abs_y(&self) -> Option<input_absinfo> {
self.info.abs_y()
}
pub fn open(device: &str) -> io::Result<Self> {
let fd = sys::open(device, libc::O_RDONLY | libc::O_NONBLOCK)
.map_err(|error| io::Error::new(error.kind(), format!("Unable to open {device} code {error}")))?;
let fd = unsafe { OwnedFd::from_raw_fd(fd) };
Self::new(fd).map_err(|error| {
io::Error::new(error.kind(), format!("Unable to initialize evdev for {device} code {error}"))
})
}
pub fn new(fd: OwnedFd) -> io::Result<Self> {
let raw = fd.as_raw_fd();
let mut name = [0u8; 256];
sys::ioctl_buffer(raw, eviocgname(name.len()), &mut name)?;
let len = name.iter().position(|&b| b == 0).unwrap_or(name.len());
let name = Some(String::from_utf8_lossy(&name[..len]).into_owned());
let mut bits = [0u8; 4];
let _ = sys::ioctl_buffer(raw, eviocgbit(0, bits.len()), &mut bits);
let has = |bits: &[u8], bit: u16| bits.get(bit as usize / 8).is_some_and(|b| b & (1 << (bit % 8)) != 0);
let event_types = ev_type::ALL.into_iter().filter(|&type_| has(&bits, type_)).collect();
let mut abs_bits = [0u8; 8];
let _ = sys::ioctl_buffer(raw, eviocgbit(ev_type::EV_ABS as u32, abs_bits.len()), &mut abs_bits);
let mut abs = HashMap::new();
for code in AXES {
if !has(&abs_bits, code) {
continue;
}
let mut absinfo = input_absinfo::default();
if sys::ioctl_struct(raw, eviocgabs(code as u32), &mut absinfo).is_ok() {
abs.insert(code, absinfo);
}
}
Ok(Self { fd, info: EvDevDeviceInfo { name, event_types, abs } })
}
pub fn read_events(fd: RawFd) -> Vec<input_event> {
let mut rv = Vec::new();
loop {
let mut buffer: [libc::input_event; 64] = unsafe { std::mem::zeroed() };
let size = std::mem::size_of::<libc::input_event>();
let read = unsafe { libc::read(fd, buffer.as_mut_ptr().cast(), size * buffer.len()) };
if read <= 0 {
break;
}
for ev in &buffer[..read as usize / size] {
rv.push(input_event {
tv_sec: ev.time.tv_sec as i64,
tv_usec: ev.time.tv_usec as i64,
type_: ev.type_,
code: ev.code,
value: ev.value,
});
}
}
rv
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn the_request_numbers_are_those_of_the_kernel() {
assert_eq!(eviocgname(256), 0x8100_4506);
assert_eq!(eviocgbit(0, 4), 0x8004_4520);
assert_eq!(eviocgabs(0), 0x8018_4540);
assert_eq!(eviocgabs(0x35), 0x8018_4575);
}
}
}