diff --git a/input/build.rs b/input/build.rs index 6a8769e..e243b79 100644 --- a/input/build.rs +++ b/input/build.rs @@ -4,6 +4,7 @@ use std::path::PathBuf; fn main() { println!("cargo:rerun-if-changed=glue/glue.h"); + println!("cargo:rustc-link-lib=evdev"); let bindings = Builder::default() .header("glue/glue.h") diff --git a/input/glue/glue.h b/input/glue/glue.h index 9e3ed63..648e256 100644 --- a/input/glue/glue.h +++ b/input/glue/glue.h @@ -1 +1 @@ -#include \ No newline at end of file +#include \ No newline at end of file diff --git a/input/src/event_manager.rs b/input/src/event_manager.rs index 081355d..35463f8 100644 --- a/input/src/event_manager.rs +++ b/input/src/event_manager.rs @@ -1,5 +1,5 @@ use crate::event::Event; -use crate::event_reader::{EventReader, OpenError}; +use crate::event_reader::EventReader; use crate::event_writer::EventWriter; use crate::glue::input_event; use std::io::{Error, ErrorKind}; @@ -21,11 +21,17 @@ impl EventManager { continue; } - let reader = match EventReader::new(&path).await { - Ok(reader) => reader, - Err(OpenError::NotSupported) => continue, - Err(OpenError::Io(err)) => return Err(err), - }; + // Skip non input event files. + if path + .file_name() + .and_then(|name| name.to_str()) + .map(|name| !name.starts_with("event")) + .unwrap_or(true) + { + continue; + } + + let reader = EventReader::new(&path).await?; let sender = sender.clone(); tokio::spawn(handle_events(reader, sender)); diff --git a/input/src/event_reader.rs b/input/src/event_reader.rs index c0be747..2beb2ce 100644 --- a/input/src/event_reader.rs +++ b/input/src/event_reader.rs @@ -1,21 +1,127 @@ -use crate::glue::input_event; +use crate::glue::{self, input_event, libevdev}; +use mio::event::Evented; +use mio::unix::EventedFd; +use mio::{PollOpt, Ready, Token}; +use std::fs::{File, OpenOptions}; +use std::future::Future; use std::io::Error; +use std::mem::MaybeUninit; +use std::os::unix::fs::OpenOptionsExt; +use std::os::unix::io::AsRawFd; use std::path::Path; +use std::pin::Pin; +use std::task::{Context, Poll}; +use tokio::io::Registration; -pub(crate) struct EventReader {} +pub(crate) struct EventReader { + file: EventedFile, + registration: Registration, + evdev: *mut libevdev, +} impl EventReader { - pub async fn new(path: &Path) -> Result { - todo!() + pub async fn new(path: &Path) -> Result { + let path = path.to_owned(); + + tokio::task::spawn_blocking(move || Self::open_sync(&path)).await? } pub async fn read(&mut self) -> Result { - todo!() + Read { reader: self }.await + } + + fn open_sync(path: &Path) -> Result { + let file = OpenOptions::new() + .read(true) + .custom_flags(libc::O_NONBLOCK) + .open(path) + .map(|file| EventedFile { file })?; + let registration = Registration::new(&file)?; + + let mut evdev = std::ptr::null_mut(); + + let ret = + unsafe { glue::libevdev_new_from_fd(file.file.as_raw_fd(), &mut evdev as *mut _) }; + if ret < 0 { + return Err(Error::from_raw_os_error(-ret)); + } + + let ret = unsafe { glue::libevdev_grab(evdev, glue::libevdev_grab_mode_LIBEVDEV_GRAB) }; + if ret < 0 { + unsafe { glue::libevdev_free(evdev) }; + return Err(Error::from_raw_os_error(-ret)); + } + + Ok(Self { + file, + registration, + evdev, + }) } } -#[derive(Debug)] -pub enum OpenError { - NotSupported, - Io(Error), +unsafe impl Send for EventReader {} + +impl Drop for EventReader { + fn drop(&mut self) { + unsafe { glue::libevdev_free(self.evdev) }; + } +} + +struct EventedFile { + file: File, +} + +impl Evented for EventedFile { + fn register( + &self, + poll: &mio::Poll, + token: Token, + interest: Ready, + opts: PollOpt, + ) -> Result<(), Error> { + EventedFd(&self.file.as_raw_fd()).register(poll, token, interest, opts) + } + + fn reregister( + &self, + poll: &mio::Poll, + token: Token, + interest: Ready, + opts: PollOpt, + ) -> Result<(), Error> { + EventedFd(&self.file.as_raw_fd()).reregister(poll, token, interest, opts) + } + + fn deregister(&self, poll: &mio::Poll) -> Result<(), Error> { + EventedFd(&self.file.as_raw_fd()).deregister(poll) + } +} + +struct Read<'a> { + reader: &'a mut EventReader, +} + +impl Future for Read<'_> { + type Output = Result; + + fn poll(self: Pin<&mut Self>, context: &mut Context) -> Poll { + if let Poll::Pending = self.reader.registration.poll_read_ready(context)? { + return Poll::Pending; + } + + let mut event = MaybeUninit::uninit(); + let ret = unsafe { + glue::libevdev_next_event( + self.reader.evdev, + glue::libevdev_read_flag_LIBEVDEV_READ_FLAG_NORMAL, + event.as_mut_ptr(), + ) + }; + if ret < 0 { + return Poll::Ready(Err(Error::from_raw_os_error(-ret))); + } + + Poll::Ready(Ok(unsafe { event.assume_init() })) + } }