use std::{
error::Error,
ffi::{c_char, c_int, c_uint, c_void},
fmt,
fs::File,
io,
mem::MaybeUninit,
os::{
fd::{AsFd, AsRawFd, IntoRawFd, OwnedFd},
unix::prelude::{BorrowedFd, RawFd},
},
path::Path,
slice,
time::Instant,
};
use libc::clockid_t;
use uoctl::Ioctl;
use crate::{
AbsInfo, InputProp, KeyRepeat, KeymapEntry, Version,
bits::{BitSet, BitValue, Word},
event::{
Abs, EventType, ForceFeedbackEvent, InputEvent, Key, Led, LedEvent, Misc, Rel, Sound,
Switch,
},
ff,
input_id::InputId,
keymap_entry::Scancode,
raw::input::{
EVIOCGABS, EVIOCGBIT, EVIOCGEFFECTS, EVIOCGID, EVIOCGKEY, EVIOCGKEYCODE_V2, EVIOCGLED,
EVIOCGMASK, EVIOCGNAME, EVIOCGPHYS, EVIOCGPROP, EVIOCGRAB, EVIOCGREP, EVIOCGSND, EVIOCGSW,
EVIOCGUNIQ, EVIOCGVERSION, EVIOCREVOKE, EVIOCRMFF, EVIOCSABS, EVIOCSCLOCKID, EVIOCSFF,
EVIOCSKEYCODE_V2, EVIOCSMASK, EVIOCSREP, INPUT_KEYMAP_BY_INDEX, input_mask,
},
read_raw,
reader::EventReader,
util::{block_until_readable, is_readable, set_nonblocking},
write_raw,
};
#[derive(Debug)]
pub struct Evdev {
pub(crate) file: File,
}
impl AsFd for Evdev {
#[inline]
fn as_fd(&self) -> BorrowedFd<'_> {
self.file.as_fd()
}
}
impl AsRawFd for Evdev {
#[inline]
fn as_raw_fd(&self) -> RawFd {
self.file.as_raw_fd()
}
}
impl IntoRawFd for Evdev {
#[inline]
fn into_raw_fd(self) -> RawFd {
self.file.into_raw_fd()
}
}
impl From<Evdev> for OwnedFd {
#[inline]
fn from(value: Evdev) -> Self {
value.file.into()
}
}
impl Evdev {
pub fn open<P: AsRef<Path>>(path: P) -> io::Result<Self> {
let path = path.as_ref();
Self::open_impl(path)
}
fn open_impl(path: &Path) -> io::Result<Self> {
const PREFIX: &[u8] = b"/dev/input/event";
if path.as_os_str().as_encoded_bytes().starts_with(PREFIX) {
return Self::open_unchecked(path);
}
let path = path.canonicalize()?;
if !path
.as_os_str()
.as_encoded_bytes()
.starts_with(b"/dev/input/event")
{
return Err(io::Error::new(
io::ErrorKind::InvalidInput,
format!(
"evdev device path '{}' must match '/dev/input/event*'",
path.display()
),
));
}
Self::open_unchecked(&path)
}
pub(crate) fn open_unchecked(path: &Path) -> io::Result<Self> {
let now = Instant::now();
let file = match Self::try_open(path) {
Ok(file) => file,
Err(e) => {
return Err(io::Error::new(
e.kind(),
format!("failed to open '{}': {e}", path.display()),
));
}
};
let this = Self { file };
let version = this.driver_version()?;
log::debug!(
"opened '{}' in {:?}; driver version {version}",
path.display(),
now.elapsed(),
);
Ok(this)
}
fn try_open(path: &Path) -> io::Result<File> {
match File::options().read(true).write(true).open(path) {
Ok(file) => return Ok(file),
Err(e) if e.kind() == io::ErrorKind::PermissionDenied => {
log::warn!(
"no permission to open '{}' in read-write mode, retrying in read-only",
path.display()
);
}
Err(e) => return Err(e),
}
match File::options().read(true).open(path) {
Ok(file) => return Ok(file),
Err(e) if e.kind() == io::ErrorKind::PermissionDenied => {
log::warn!(
"no permission to open '{}' in read-only mode, retrying in write-only",
path.display()
);
}
Err(e) => return Err(e),
}
File::options().write(true).open(path)
}
#[inline]
pub unsafe fn from_owned_fd(owned_fd: OwnedFd) -> Self {
Self {
file: File::from(owned_fd),
}
}
pub fn set_nonblocking(&self, nonblocking: bool) -> io::Result<bool> {
set_nonblocking(self.as_raw_fd(), nonblocking)
}
#[doc(alias = "dup")]
pub fn try_clone(&self) -> io::Result<Self> {
Ok(Self {
file: self.file.try_clone()?,
})
}
pub(crate) unsafe fn ioctl<T>(
&self,
name: &'static str,
ioctl: Ioctl<T>,
arg: T,
) -> io::Result<c_int> {
match unsafe { ioctl.ioctl(self, arg) } {
Ok(ok) => Ok(ok),
Err(e) => {
#[derive(Debug)]
struct WrappedError {
cause: io::Error,
ioctl: &'static str,
}
impl fmt::Display for WrappedError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "ioctl {} failed", self.ioctl)?;
if let Some(code) = self.cause.raw_os_error() {
write!(f, " with error code {code}")?;
}
write!(f, " ({})", self.cause.kind())
}
}
impl Error for WrappedError {
fn source(&self) -> Option<&(dyn Error + 'static)> {
Some(&self.cause)
}
}
if e.raw_os_error() == Some(libc::ENODEV) {
Err(e)
} else {
Err(io::Error::new(
e.kind(),
WrappedError {
cause: e,
ioctl: name,
},
))
}
}
}
}
unsafe fn fetch_string(
&self,
ioctl_name: &'static str,
ioctl: fn(usize) -> Ioctl<*mut c_char>,
) -> io::Result<String> {
const INITIAL_LEN: usize = 64;
let mut buf = vec![0_u8; INITIAL_LEN];
let len = loop {
let len = unsafe {
self.ioctl(
ioctl_name,
ioctl(buf.len()),
buf.as_mut_ptr() as *mut c_char,
)?
};
if len as usize == buf.len() {
buf.resize(buf.len() * 2, 0);
} else {
break len;
}
};
buf.truncate(len.saturating_sub(1) as usize);
let string =
String::from_utf8(buf).map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?;
Ok(string)
}
unsafe fn fetch_bits<V: BitValue>(
&self,
ioctl_name: &'static str,
ioctl: fn(usize) -> Ioctl<*mut c_void>,
) -> io::Result<BitSet<V>> {
let mut set = BitSet::<V>::new();
let words = set.words_mut();
unsafe {
self.ioctl(
ioctl_name,
ioctl(words.len() * size_of::<Word>()),
words.as_mut_ptr().cast(),
)?;
};
Ok(set)
}
#[doc(alias = "EVIOCGVERSION")]
pub fn driver_version(&self) -> io::Result<Version> {
unsafe {
let mut version = 0;
self.ioctl("EVIOCGVERSION", EVIOCGVERSION, &mut version)?;
Ok(Version(version))
}
}
#[doc(alias = "EVIOCGID")]
pub fn input_id(&self) -> io::Result<InputId> {
let mut out = MaybeUninit::uninit();
unsafe {
self.ioctl("EVIOCGID", EVIOCGID, out.as_mut_ptr())?;
Ok(InputId(out.assume_init()))
}
}
#[doc(alias = "EVIOCGNAME")]
pub fn name(&self) -> io::Result<String> {
unsafe { self.fetch_string("EVIOCGNAME", EVIOCGNAME) }
}
#[doc(alias = "EVIOCGPHYS")]
pub fn phys(&self) -> io::Result<Option<String>> {
unsafe {
match self.fetch_string("EVIOCGPHYS", EVIOCGPHYS) {
Ok(loc) => Ok(Some(loc)),
Err(e) if e.kind() == io::ErrorKind::NotFound => Ok(None),
Err(e) => Err(e),
}
}
}
#[doc(alias = "EVIOCGUNIQ")]
pub fn unique_id(&self) -> io::Result<Option<String>> {
unsafe {
match self.fetch_string("EVIOCGUNIQ", EVIOCGUNIQ) {
Ok(loc) => Ok(Some(loc)),
Err(e) if e.kind() == io::ErrorKind::NotFound => Ok(None),
Err(e) => Err(e),
}
}
}
#[doc(alias = "EVIOCGPROP")]
pub fn props(&self) -> io::Result<BitSet<InputProp>> {
unsafe { self.fetch_bits("EVIOCGPROP", EVIOCGPROP) }
}
#[doc(alias = "EVIOCGBIT")]
pub fn supported_events(&self) -> io::Result<BitSet<EventType>> {
unsafe { self.fetch_bits("EVIOCGBIT", |len| EVIOCGBIT(0, len)) }
}
pub fn supported_keys(&self) -> io::Result<BitSet<Key>> {
unsafe { self.fetch_bits("EVIOCGBIT", |len| EVIOCGBIT(EventType::KEY.0 as u8, len)) }
}
pub fn supported_switches(&self) -> io::Result<BitSet<Switch>> {
unsafe { self.fetch_bits("EVIOCGBIT", |len| EVIOCGBIT(EventType::SW.0 as u8, len)) }
}
pub fn supported_leds(&self) -> io::Result<BitSet<Led>> {
unsafe { self.fetch_bits("EVIOCGBIT", |len| EVIOCGBIT(EventType::LED.0 as u8, len)) }
}
pub fn supported_sounds(&self) -> io::Result<BitSet<Sound>> {
unsafe { self.fetch_bits("EVIOCGBIT", |len| EVIOCGBIT(EventType::SND.0 as u8, len)) }
}
pub fn supported_misc(&self) -> io::Result<BitSet<Misc>> {
unsafe { self.fetch_bits("EVIOCGBIT", |len| EVIOCGBIT(EventType::MSC.0 as u8, len)) }
}
pub fn supported_rel_axes(&self) -> io::Result<BitSet<Rel>> {
unsafe { self.fetch_bits("EVIOCGBIT", |len| EVIOCGBIT(EventType::REL.0 as u8, len)) }
}
pub fn supported_abs_axes(&self) -> io::Result<BitSet<Abs>> {
unsafe { self.fetch_bits("EVIOCGBIT", |len| EVIOCGBIT(EventType::ABS.0 as u8, len)) }
}
pub fn supported_ff_features(&self) -> io::Result<BitSet<ff::Feature>> {
unsafe { self.fetch_bits("EVIOCGBIT", |len| EVIOCGBIT(EventType::FF.0 as u8, len)) }
}
#[doc(alias = "EVIOCGEFFECTS")]
pub fn supported_ff_effects(&self) -> io::Result<u32> {
unsafe {
let mut out = 0;
self.ioctl("EVIOCGEFFECTS", EVIOCGEFFECTS, &mut out)?;
Ok(out.try_into().unwrap())
}
}
#[doc(alias = "EVIOCGABS")]
pub fn abs_info(&self, abs: Abs) -> io::Result<AbsInfo> {
if abs.0 > Abs::MAX.0 {
return Err(io::Error::new(
io::ErrorKind::InvalidInput,
format!("absolute axis {:?} exceeds maximum axis value", abs),
));
}
unsafe {
let mut out = MaybeUninit::uninit();
self.ioctl("EVIOCGABS", EVIOCGABS(abs.0 as u8), out.as_mut_ptr())?;
Ok(AbsInfo(out.assume_init()))
}
}
#[doc(alias = "EVIOCSABS")]
pub fn set_abs_info(&self, abs: Abs, info: AbsInfo) -> io::Result<()> {
if abs.0 > Abs::MAX.0 {
return Err(io::Error::new(
io::ErrorKind::InvalidInput,
format!("absolute axis {:?} exceeds maximum axis value", abs),
));
}
unsafe {
self.ioctl("EVIOCSABS", EVIOCSABS(abs.raw() as u8), &info.0)?;
}
Ok(())
}
#[doc(alias = "EVIOCGRAB")]
pub fn grab(&self) -> io::Result<()> {
unsafe {
self.ioctl("EVIOCGRAB", EVIOCGRAB, 1)?;
Ok(())
}
}
#[doc(alias = "EVIOCGRAB")]
pub fn ungrab(&self) -> io::Result<()> {
unsafe {
self.ioctl("EVIOCGRAB", EVIOCGRAB, 0)?;
Ok(())
}
}
#[doc(alias = "EVIOCREVOKE")]
pub fn revoke(&self) -> io::Result<()> {
unsafe {
self.ioctl("EVIOCREVOKE", EVIOCREVOKE, 0)?;
Ok(())
}
}
#[doc(alias = "EVIOCGREP")]
pub fn key_repeat(&self) -> io::Result<Option<KeyRepeat>> {
unsafe {
let mut rep = [0; 2];
match self.ioctl("EVIOCGREP", EVIOCGREP, &mut rep) {
Ok(_) => Ok(Some(KeyRepeat {
delay: rep[0] as u32,
period: rep[1] as u32,
})),
Err(e) if e.kind() == io::ErrorKind::Unsupported => Ok(None),
Err(e) => Err(e),
}
}
}
#[doc(alias = "EVIOCSREP")]
pub fn set_key_repeat(&self, rep: KeyRepeat) -> io::Result<()> {
unsafe {
let rep = [rep.delay() as c_uint, rep.period() as c_uint];
self.ioctl("EVIOCSREP", EVIOCSREP, &rep)?;
Ok(())
}
}
#[doc(alias = "EVIOCGKEYCODE_V2")]
pub fn keymap_entry(&self, code: Scancode) -> io::Result<Option<KeymapEntry>> {
unsafe {
let mut entry = KeymapEntry::zeroed();
entry.0.len = code.len;
entry.0.scancode = code.bytes;
match self.ioctl("EVIOCGKEYCODE_V2", EVIOCGKEYCODE_V2, &mut entry.0) {
Ok(_) => Ok(Some(entry)),
Err(e) if e.kind() == io::ErrorKind::InvalidInput => Ok(None),
Err(e) => Err(e),
}
}
}
#[doc(alias = "EVIOCGKEYCODE_V2")]
pub fn keymap_entry_by_index(&self, index: u16) -> io::Result<Option<KeymapEntry>> {
unsafe {
let mut entry = KeymapEntry::zeroed();
entry.0.index = index;
entry.0.flags = INPUT_KEYMAP_BY_INDEX;
match self.ioctl("EVIOCGKEYCODE_V2", EVIOCGKEYCODE_V2, &mut entry.0) {
Ok(_) => Ok(Some(entry)),
Err(e) if e.kind() == io::ErrorKind::InvalidInput => Ok(None),
Err(e) => Err(e),
}
}
}
#[doc(alias = "EVIOCSKEYCODE_V2")]
pub fn set_keymap_entry(&self, scancode: Scancode, keycode: Key) -> io::Result<()> {
unsafe {
let mut entry = KeymapEntry::zeroed();
entry.0.keycode = keycode.raw().into();
entry.0.len = scancode.len;
entry.0.scancode = scancode.bytes;
self.ioctl("EVIOCSKEYCODE_V2", EVIOCSKEYCODE_V2, &entry.0)?;
Ok(())
}
}
#[doc(alias = "EVIOCSKEYCODE_V2")]
pub fn set_keymap_entry_by_index(&self, index: u16, keycode: Key) -> io::Result<()> {
unsafe {
let mut entry = KeymapEntry::zeroed();
entry.0.flags = INPUT_KEYMAP_BY_INDEX;
entry.0.keycode = keycode.raw().into();
entry.0.index = index;
self.ioctl("EVIOCSKEYCODE_V2", EVIOCSKEYCODE_V2, &entry.0)?;
Ok(())
}
}
#[doc(alias = "EVIOCGKEY")]
pub fn key_state(&self) -> io::Result<BitSet<Key>> {
unsafe { self.fetch_bits("EVIOCGKEY", EVIOCGKEY) }
}
#[doc(alias = "EVIOCGLED")]
pub fn led_state(&self) -> io::Result<BitSet<Led>> {
unsafe { self.fetch_bits("EVIOCGLED", EVIOCGLED) }
}
#[doc(alias = "EVIOCGSND")]
pub fn sound_state(&self) -> io::Result<BitSet<Sound>> {
unsafe { self.fetch_bits("EVIOCGSND", EVIOCGSND) }
}
#[doc(alias = "EVIOCGSW")]
pub fn switch_state(&self) -> io::Result<BitSet<Switch>> {
unsafe { self.fetch_bits("EVIOCGSW", EVIOCGSW) }
}
pub fn into_reader(self) -> io::Result<EventReader> {
EventReader::new(self)
}
pub fn is_readable(&self) -> io::Result<bool> {
is_readable(self.as_raw_fd())
}
pub fn block_until_readable(&self) -> io::Result<()> {
block_until_readable(self.as_raw_fd())
}
#[inline]
pub fn raw_events(&self) -> RawEvents<'_> {
RawEvents { file: &self.file }
}
pub fn read_events(&self, buf: &mut [InputEvent]) -> io::Result<usize> {
read_raw(&self.file, buf)
}
#[doc(alias = "EVIOCSFF")]
pub fn upload_ff_effect<'a>(
&self,
effect: impl Into<ff::Effect<'a>>,
) -> io::Result<ff::EffectId> {
self.upload_ff_effect_impl(effect.into())
}
fn upload_ff_effect_impl(&self, mut effect: ff::Effect<'_>) -> io::Result<ff::EffectId> {
log::trace!("uploading FF effect: {:?}", effect);
let now = Instant::now();
unsafe {
self.ioctl("EVIOCSFF", EVIOCSFF, &mut effect.raw)?;
}
log::debug!("upload_ff_effect: ioctl took {:?}", now.elapsed());
Ok(ff::EffectId(effect.raw.id))
}
#[doc(alias = "EVIOCRMFF")]
pub fn erase_ff_effect(&self, id: ff::EffectId) -> io::Result<()> {
unsafe {
self.ioctl("EVIOCRMFF", EVIOCRMFF, id.0 as c_int)?;
}
Ok(())
}
pub fn set_led(&self, led: Led, on: bool) -> io::Result<()> {
self.write(&[LedEvent::new(led, on).into()])
}
pub fn control_ff(&self, effect: ff::EffectId, active: bool) -> io::Result<()> {
self.write(&[ForceFeedbackEvent::control_effect(effect, active).into()])
}
pub fn set_ff_gain(&self, gain: u16) -> io::Result<()> {
self.write(&[ForceFeedbackEvent::control_gain(gain).into()])
}
pub fn set_ff_autocenter(&self, autocenter: u16) -> io::Result<()> {
self.write(&[ForceFeedbackEvent::control_autocenter(autocenter).into()])
}
pub fn write(&self, events: &[InputEvent]) -> io::Result<()> {
write_raw(&self.file, events)
}
#[doc(alias = "EVIOCSCLOCKID")]
pub fn set_clockid(&self, clockid: clockid_t) -> io::Result<()> {
unsafe {
self.ioctl("EVIOCSCLOCKID", EVIOCSCLOCKID, &clockid)?;
Ok(())
}
}
}
impl Evdev {
fn fetch_mask<V: BitValue>(&self, ty: EventType) -> io::Result<BitSet<V>> {
let mut set = BitSet::<V>::new();
let words = set.words_mut();
unsafe {
let mut mask = input_mask {
type_: ty.0.into(),
codes_size: (words.len() * size_of::<Word>()) as u32,
codes_ptr: words.as_mut_ptr().expose_provenance() as u64,
};
self.ioctl("EVIOCGMASK", EVIOCGMASK, &mut mask)?;
}
Ok(set)
}
fn set_mask<V: BitValue>(&self, ty: EventType, mask: &BitSet<V>) -> io::Result<()> {
let words = mask.words();
unsafe {
let mask = input_mask {
type_: ty.0.into(),
codes_size: (words.len() * size_of::<Word>()) as u32,
codes_ptr: words.as_ptr().expose_provenance() as u64,
};
self.ioctl("EVIOCSMASK", EVIOCSMASK, &mask)?;
}
Ok(())
}
#[doc(alias = "EVIOCGMASK")]
pub fn event_mask(&self) -> io::Result<BitSet<EventType>> {
self.fetch_mask(EventType::from_raw(0))
}
#[doc(alias = "EVIOCSMASK")]
pub fn set_event_mask(&self, mask: &BitSet<EventType>) -> io::Result<()> {
self.set_mask(EventType::from_raw(0), mask)
}
pub fn key_mask(&self) -> io::Result<BitSet<Key>> {
self.fetch_mask(EventType::KEY)
}
pub fn set_key_mask(&self, mask: &BitSet<Key>) -> io::Result<()> {
self.set_mask(EventType::KEY, mask)
}
pub fn rel_mask(&self) -> io::Result<BitSet<Rel>> {
self.fetch_mask(EventType::REL)
}
pub fn set_rel_mask(&self, mask: &BitSet<Rel>) -> io::Result<()> {
self.set_mask(EventType::REL, mask)
}
pub fn abs_mask(&self) -> io::Result<BitSet<Abs>> {
self.fetch_mask(EventType::ABS)
}
pub fn set_abs_mask(&self, mask: &BitSet<Abs>) -> io::Result<()> {
self.set_mask(EventType::ABS, mask)
}
pub fn switch_mask(&self) -> io::Result<BitSet<Switch>> {
self.fetch_mask(EventType::SW)
}
pub fn set_switch_mask(&self, mask: &BitSet<Switch>) -> io::Result<()> {
self.set_mask(EventType::SW, mask)
}
}
#[derive(Debug)]
pub struct RawEvents<'a> {
pub(crate) file: &'a File,
}
impl Iterator for RawEvents<'_> {
type Item = io::Result<InputEvent>;
fn next(&mut self) -> Option<Self::Item> {
let mut dest = InputEvent::zeroed();
match read_raw(&self.file, slice::from_mut(&mut dest)) {
Err(e) if e.kind() == io::ErrorKind::WouldBlock => None,
Err(e) => Some(Err(e)),
Ok(0) => None,
Ok(1) => Some(Ok(dest)),
Ok(n) => unreachable!("read {n} events, but can only hold 1"),
}
}
}