use std::{
io, mem,
os::fd::{AsRawFd, OwnedFd},
};
use nix::{
fcntl::{OFlag, open},
libc::{c_char, input_absinfo, input_event, input_id, timeval, uinput_abs_setup, uinput_setup},
sys::stat::Mode,
unistd::write,
};
use uinput_ioctls::{
ui_abs_setup, ui_dev_create, ui_dev_destroy, ui_dev_setup, ui_set_absbit, ui_set_evbit,
ui_set_keybit, ui_set_relbit,
};
pub const EV_SYN: u16 = 0x00;
pub const EV_KEY: u16 = 0x01;
pub const EV_REL: u16 = 0x02;
pub const EV_ABS: u16 = 0x03;
pub const SYN_REPORT: u16 = 0;
pub const REL_HWHEEL: u16 = 0x06;
pub const REL_WHEEL: u16 = 0x08;
pub const REL_WHEEL_HI_RES: u16 = 0x0b;
pub const REL_HWHEEL_HI_RES: u16 = 0x0c;
pub const ABS_X: u16 = 0x00;
pub const ABS_Y: u16 = 0x01;
pub const BTN_LEFT: u16 = 0x110;
pub const BTN_TASK: u16 = 0x117;
pub const KEY_LEFTCTRL: u16 = 29;
pub const KEY_LEFTSHIFT: u16 = 42;
pub const KEY_LEFTALT: u16 = 56;
pub const KEY_CAPSLOCK: u16 = 58;
pub const KEY_NUMLOCK: u16 = 69;
pub const KEY_RIGHTALT: u16 = 100;
pub const KEY_LEFTMETA: u16 = 125;
const BUS_VIRTUAL: u16 = 0x06;
pub const ABS_MAX_VAL: i32 = 65535;
pub struct UinputBuilder {
fd: OwnedFd,
abs: Vec<(u16, i32, i32)>,
}
impl UinputBuilder {
pub fn new() -> io::Result<Self> {
let fd = open(
"/dev/uinput",
OFlag::O_WRONLY | OFlag::O_NONBLOCK | OFlag::O_CLOEXEC,
Mode::empty(),
)?;
Ok(Self {
fd,
abs: Vec::new(),
})
}
pub fn enable_ev(&self, ev: u16) -> io::Result<()> {
unsafe { ui_set_evbit(self.fd.as_raw_fd(), ev as _) }?;
Ok(())
}
pub fn enable_key(&self, code: u16) -> io::Result<()> {
unsafe { ui_set_keybit(self.fd.as_raw_fd(), code as _) }?;
Ok(())
}
pub fn enable_rel(&self, code: u16) -> io::Result<()> {
unsafe { ui_set_relbit(self.fd.as_raw_fd(), code as _) }?;
Ok(())
}
pub fn enable_abs(&mut self, code: u16, min: i32, max: i32) -> io::Result<()> {
unsafe { ui_set_absbit(self.fd.as_raw_fd(), code as _) }?;
self.abs.push((code, min, max));
Ok(())
}
pub fn build(self, name: &str) -> io::Result<UinputDevice> {
let fd = self.fd.as_raw_fd();
for &(code, min, max) in &self.abs {
let abs = uinput_abs_setup {
code,
absinfo: input_absinfo {
value: 0,
minimum: min,
maximum: max,
fuzz: 0,
flat: 0,
resolution: 0,
},
};
unsafe { ui_abs_setup(fd, &abs) }?;
}
let mut setup: uinput_setup = unsafe { mem::zeroed() };
setup.id = input_id {
bustype: BUS_VIRTUAL,
vendor: 0x1234,
product: 0x5678,
version: 1,
};
let last = setup.name.len() - 1;
for (dst, &src) in setup.name[..last].iter_mut().zip(name.as_bytes()) {
*dst = src as c_char;
}
unsafe { ui_dev_setup(fd, &setup) }?;
unsafe { ui_dev_create(fd) }?;
Ok(UinputDevice { fd: self.fd })
}
}
pub struct UinputDevice {
fd: OwnedFd,
}
impl UinputDevice {
pub fn emit(&self, ty: u16, code: u16, value: i32) -> io::Result<()> {
let ev = input_event {
time: timeval {
tv_sec: 0,
tv_usec: 0,
},
type_: ty,
code,
value,
};
let size = mem::size_of::<input_event>();
let bytes = unsafe { std::slice::from_raw_parts((&ev as *const input_event).cast::<u8>(), size) };
let written = write(&self.fd, bytes)?;
if written != size {
return Err(io::Error::last_os_error());
}
Ok(())
}
pub fn sync(&self) -> io::Result<()> {
self.emit(EV_SYN, SYN_REPORT, 0)
}
}
impl Drop for UinputDevice {
fn drop(&mut self) {
let _ = unsafe { ui_dev_destroy(self.fd.as_raw_fd()) };
}
}
pub struct Device(pub Option<UinputDevice>);
impl Device {
pub fn emit(&self, ty: u16, code: u16, value: i32) {
if let Some(dev) = &self.0 {
let _ = dev.emit(ty, code, value);
}
}
pub fn sync(&self) {
if let Some(dev) = &self.0 {
let _ = dev.sync();
}
}
}