mirror of
https://github.com/htrefil/rkvm.git
synced 2024-11-16 07:47:24 +01:00
Use RAII wrappers for libevdev objects
This commit is contained in:
parent
cd77a50cbb
commit
0c16cdd144
5 changed files with 169 additions and 138 deletions
73
rkvm-input/src/evdev.rs
Normal file
73
rkvm-input/src/evdev.rs
Normal file
|
@ -0,0 +1,73 @@
|
|||
use crate::glue::{self, libevdev};
|
||||
|
||||
use std::fs::File;
|
||||
use std::io::{Error, ErrorKind};
|
||||
use std::mem::MaybeUninit;
|
||||
use std::os::fd::AsRawFd;
|
||||
use std::path::Path;
|
||||
use std::ptr::NonNull;
|
||||
use tokio::fs::OpenOptions;
|
||||
use tokio::io::unix::AsyncFd;
|
||||
|
||||
pub struct Evdev {
|
||||
evdev: NonNull<libevdev>,
|
||||
file: Option<AsyncFd<File>>,
|
||||
}
|
||||
|
||||
impl Evdev {
|
||||
pub fn new() -> Result<Self, Error> {
|
||||
let evdev = unsafe { glue::libevdev_new() };
|
||||
let evdev = NonNull::new(evdev)
|
||||
.ok_or_else(|| Error::new(ErrorKind::Other, "Failed to create device"))?;
|
||||
|
||||
Ok(Self { evdev, file: None })
|
||||
}
|
||||
|
||||
pub async fn open(path: &Path) -> Result<Self, Error> {
|
||||
let file = OpenOptions::new()
|
||||
.read(true)
|
||||
.write(true)
|
||||
.custom_flags(libc::O_NONBLOCK)
|
||||
.open(path)
|
||||
.await?
|
||||
.into_std()
|
||||
.await;
|
||||
|
||||
let file = AsyncFd::new(file)?;
|
||||
|
||||
let mut evdev = MaybeUninit::uninit();
|
||||
|
||||
let ret = unsafe { glue::libevdev_new_from_fd(file.as_raw_fd(), evdev.as_mut_ptr()) };
|
||||
if ret < 0 {
|
||||
return Err(Error::from_raw_os_error(-ret).into());
|
||||
}
|
||||
|
||||
let evdev = unsafe { evdev.assume_init() };
|
||||
let evdev = unsafe { NonNull::new_unchecked(evdev) };
|
||||
|
||||
Ok(Self {
|
||||
evdev,
|
||||
file: Some(file),
|
||||
})
|
||||
}
|
||||
|
||||
pub fn file(&self) -> Option<&AsyncFd<File>> {
|
||||
self.file.as_ref()
|
||||
}
|
||||
|
||||
pub fn as_ptr(&self) -> *mut libevdev {
|
||||
self.evdev.as_ptr()
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for Evdev {
|
||||
fn drop(&mut self) {
|
||||
unsafe {
|
||||
glue::libevdev_free(self.evdev.as_ptr());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl Send for Evdev {}
|
||||
|
||||
unsafe impl Sync for Evdev {}
|
|
@ -3,8 +3,9 @@ mod caps;
|
|||
pub use caps::{AbsCaps, KeyCaps, RelCaps};
|
||||
|
||||
use crate::abs::{AbsAxis, AbsEvent, ToolType};
|
||||
use crate::evdev::Evdev;
|
||||
use crate::event::Event;
|
||||
use crate::glue::{self, libevdev};
|
||||
use crate::glue;
|
||||
use crate::key::{Key, KeyEvent};
|
||||
use crate::registry::{Entry, Handle, Registry};
|
||||
use crate::rel::{RelAxis, RelEvent};
|
||||
|
@ -13,21 +14,14 @@ use crate::writer::Writer;
|
|||
|
||||
use std::collections::VecDeque;
|
||||
use std::ffi::CStr;
|
||||
use std::fs::{File, OpenOptions};
|
||||
use std::io::{Error, ErrorKind};
|
||||
use std::mem::MaybeUninit;
|
||||
use std::os::fd::AsRawFd;
|
||||
use std::os::unix::fs::MetadataExt;
|
||||
use std::os::unix::prelude::OpenOptionsExt;
|
||||
use std::path::Path;
|
||||
use std::ptr::NonNull;
|
||||
use thiserror::Error;
|
||||
use tokio::io::unix::AsyncFd;
|
||||
use tokio::task;
|
||||
|
||||
pub struct Interceptor {
|
||||
file: AsyncFd<File>,
|
||||
evdev: NonNull<libevdev>,
|
||||
evdev: Evdev,
|
||||
writer: Writer,
|
||||
// The state of `read` is stored here to make it cancel safe.
|
||||
events: VecDeque<Event>,
|
||||
|
@ -142,8 +136,10 @@ impl Interceptor {
|
|||
}
|
||||
|
||||
async fn read_raw(&mut self) -> Result<(u16, u16, i32), Error> {
|
||||
let file = self.evdev.file().unwrap();
|
||||
|
||||
loop {
|
||||
let result = self.file.readable().await?.try_io(|_| {
|
||||
let result = file.readable().await?.try_io(|_| {
|
||||
let mut event = MaybeUninit::uninit();
|
||||
let ret = unsafe {
|
||||
glue::libevdev_next_event(
|
||||
|
@ -179,22 +175,9 @@ impl Interceptor {
|
|||
}
|
||||
|
||||
pub(crate) async fn open(path: &Path, registry: &Registry) -> Result<Self, OpenError> {
|
||||
let path = path.to_owned();
|
||||
let registry = registry.clone();
|
||||
let evdev = Evdev::open(path).await?;
|
||||
let metadata = evdev.file().unwrap().get_ref().metadata()?;
|
||||
|
||||
task::spawn_blocking(move || Self::open_sync(&path, ®istry))
|
||||
.await
|
||||
.map_err(|err| OpenError::Io(err.into()))?
|
||||
}
|
||||
|
||||
fn open_sync(path: &Path, registry: &Registry) -> Result<Self, OpenError> {
|
||||
let file = OpenOptions::new()
|
||||
.read(true)
|
||||
.custom_flags(libc::O_NONBLOCK)
|
||||
.open(path)
|
||||
.and_then(AsyncFd::new)?;
|
||||
|
||||
let metadata = file.get_ref().metadata()?;
|
||||
let reader_handle = registry
|
||||
.register(Entry {
|
||||
device: metadata.dev(),
|
||||
|
@ -202,26 +185,12 @@ impl Interceptor {
|
|||
})
|
||||
.ok_or(OpenError::NotAppliable)?;
|
||||
|
||||
let mut evdev = MaybeUninit::uninit();
|
||||
|
||||
let ret = unsafe { glue::libevdev_new_from_fd(file.as_raw_fd(), evdev.as_mut_ptr()) };
|
||||
if ret < 0 {
|
||||
return Err(Error::from_raw_os_error(-ret).into());
|
||||
}
|
||||
|
||||
let evdev = unsafe { evdev.assume_init() };
|
||||
let evdev = NonNull::new(evdev).unwrap();
|
||||
|
||||
// "Upon binding to a device or resuming from suspend, a driver must report
|
||||
// the current switch state. This ensures that the device, kernel, and userspace
|
||||
// state is in sync."
|
||||
// We have no way of knowing that.
|
||||
let sw = unsafe { glue::libevdev_has_event_type(evdev.as_ptr(), glue::EV_SW) };
|
||||
if sw == 1 {
|
||||
unsafe {
|
||||
glue::libevdev_free(evdev.as_ptr());
|
||||
}
|
||||
|
||||
return Err(OpenError::NotAppliable);
|
||||
}
|
||||
|
||||
|
@ -250,10 +219,6 @@ impl Interceptor {
|
|||
unsafe { glue::libevdev_disable_event_code(evdev.as_ptr(), glue::EV_ABS, i) };
|
||||
|
||||
if ret < 0 {
|
||||
unsafe {
|
||||
glue::libevdev_free(evdev.as_ptr());
|
||||
}
|
||||
|
||||
return Err(Error::from_raw_os_error(-ret).into());
|
||||
}
|
||||
}
|
||||
|
@ -267,41 +232,17 @@ impl Interceptor {
|
|||
unsafe { glue::libevdev_grab(evdev.as_ptr(), glue::libevdev_grab_mode_LIBEVDEV_GRAB) };
|
||||
|
||||
if ret < 0 {
|
||||
unsafe {
|
||||
glue::libevdev_free(evdev.as_ptr());
|
||||
}
|
||||
|
||||
return Err(Error::from_raw_os_error(-ret).into());
|
||||
}
|
||||
|
||||
let writer = unsafe { Writer::from_evdev(evdev.as_ptr()) };
|
||||
let writer = match writer {
|
||||
Ok(writer) => writer,
|
||||
Err(err) => {
|
||||
unsafe {
|
||||
glue::libevdev_free(evdev.as_ptr());
|
||||
}
|
||||
let writer = Writer::from_evdev(&evdev).await?;
|
||||
let entry = writer.entry()?;
|
||||
|
||||
return Err(err.into());
|
||||
}
|
||||
};
|
||||
|
||||
// TODO: This is ugly. Need to refactor the evdev wrappers.
|
||||
let entry = match writer.entry() {
|
||||
Ok(entry) => entry,
|
||||
Err(err) => {
|
||||
unsafe {
|
||||
glue::libevdev_free(evdev.as_ptr());
|
||||
}
|
||||
|
||||
return Err(err.into());
|
||||
}
|
||||
};
|
||||
|
||||
let writer_handle = registry.register(entry).unwrap();
|
||||
let writer_handle = registry
|
||||
.register(entry)
|
||||
.ok_or_else(|| Error::new(ErrorKind::Other, "Writer already registered"))?;
|
||||
|
||||
Ok(Self {
|
||||
file,
|
||||
evdev,
|
||||
writer,
|
||||
events: VecDeque::new(),
|
||||
|
@ -314,14 +255,6 @@ impl Interceptor {
|
|||
}
|
||||
}
|
||||
|
||||
impl Drop for Interceptor {
|
||||
fn drop(&mut self) {
|
||||
unsafe {
|
||||
glue::libevdev_free(self.evdev.as_ptr());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl Send for Interceptor {}
|
||||
|
||||
#[derive(Error, Debug)]
|
||||
|
|
|
@ -7,5 +7,7 @@ pub mod rel;
|
|||
pub mod sync;
|
||||
pub mod writer;
|
||||
|
||||
mod evdev;
|
||||
mod glue;
|
||||
mod registry;
|
||||
mod uinput;
|
||||
|
|
69
rkvm-input/src/uinput.rs
Normal file
69
rkvm-input/src/uinput.rs
Normal file
|
@ -0,0 +1,69 @@
|
|||
use crate::evdev::Evdev;
|
||||
use crate::glue::{self, libevdev_uinput};
|
||||
|
||||
use std::fs::File;
|
||||
use std::io::Error;
|
||||
use std::mem::MaybeUninit;
|
||||
use std::os::fd::AsRawFd;
|
||||
use std::ptr::NonNull;
|
||||
use tokio::fs::OpenOptions;
|
||||
use tokio::io::unix::AsyncFd;
|
||||
|
||||
pub struct Uinput {
|
||||
file: AsyncFd<File>,
|
||||
uinput: NonNull<libevdev_uinput>,
|
||||
}
|
||||
|
||||
impl Uinput {
|
||||
pub async fn from_evdev(evdev: &Evdev) -> Result<Self, Error> {
|
||||
let file = OpenOptions::new()
|
||||
.read(true)
|
||||
.write(true)
|
||||
.custom_flags(libc::O_NONBLOCK)
|
||||
.open("/dev/uinput")
|
||||
.await?
|
||||
.into_std()
|
||||
.await;
|
||||
|
||||
let file = AsyncFd::new(file)?;
|
||||
|
||||
let mut uinput = MaybeUninit::uninit();
|
||||
|
||||
let ret = unsafe {
|
||||
glue::libevdev_uinput_create_from_device(
|
||||
evdev.as_ptr(),
|
||||
file.as_raw_fd(),
|
||||
uinput.as_mut_ptr(),
|
||||
)
|
||||
};
|
||||
|
||||
if ret < 0 {
|
||||
return Err(Error::from_raw_os_error(-ret));
|
||||
}
|
||||
|
||||
let uinput = unsafe { uinput.assume_init() };
|
||||
let uinput = unsafe { NonNull::new_unchecked(uinput) };
|
||||
|
||||
Ok(Self { file, uinput })
|
||||
}
|
||||
|
||||
pub fn file(&self) -> &AsyncFd<File> {
|
||||
&self.file
|
||||
}
|
||||
|
||||
pub fn as_ptr(&self) -> *mut libevdev_uinput {
|
||||
self.uinput.as_ptr()
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for Uinput {
|
||||
fn drop(&mut self) {
|
||||
unsafe {
|
||||
glue::libevdev_uinput_destroy(self.uinput.as_ptr());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl Send for Uinput {}
|
||||
|
||||
unsafe impl Sync for Uinput {}
|
|
@ -1,27 +1,21 @@
|
|||
use crate::abs::{AbsAxis, AbsEvent, AbsInfo};
|
||||
use crate::evdev::Evdev;
|
||||
use crate::event::Event;
|
||||
use crate::glue::{self, input_absinfo, libevdev, libevdev_uinput};
|
||||
use crate::glue::{self, input_absinfo};
|
||||
use crate::key::{Key, KeyEvent};
|
||||
use crate::registry::Entry;
|
||||
use crate::rel::{RelAxis, RelEvent};
|
||||
use crate::uinput::Uinput;
|
||||
|
||||
use std::ffi::{CStr, OsStr};
|
||||
use std::fs::{File, OpenOptions};
|
||||
use std::io::{Error, ErrorKind};
|
||||
use std::mem::MaybeUninit;
|
||||
use std::os::fd::AsRawFd;
|
||||
use std::os::unix::ffi::OsStrExt;
|
||||
use std::os::unix::fs::MetadataExt;
|
||||
use std::os::unix::prelude::OpenOptionsExt;
|
||||
use std::path::Path;
|
||||
use std::ptr::NonNull;
|
||||
use std::{fs, ptr};
|
||||
use tokio::io::unix::AsyncFd;
|
||||
use tokio::task;
|
||||
|
||||
pub struct Writer {
|
||||
file: AsyncFd<File>,
|
||||
uinput: NonNull<libevdev_uinput>,
|
||||
uinput: Uinput,
|
||||
}
|
||||
|
||||
impl Writer {
|
||||
|
@ -69,28 +63,10 @@ impl Writer {
|
|||
})
|
||||
}
|
||||
|
||||
pub(crate) unsafe fn from_evdev(evdev: *const libevdev) -> Result<Self, Error> {
|
||||
let file = OpenOptions::new()
|
||||
.read(true)
|
||||
.write(true)
|
||||
.custom_flags(libc::O_NONBLOCK)
|
||||
.open("/dev/uinput")
|
||||
.and_then(AsyncFd::new)?;
|
||||
|
||||
let mut uinput = MaybeUninit::uninit();
|
||||
|
||||
let ret = unsafe {
|
||||
glue::libevdev_uinput_create_from_device(evdev, file.as_raw_fd(), uinput.as_mut_ptr())
|
||||
};
|
||||
|
||||
if ret < 0 {
|
||||
return Err(Error::from_raw_os_error(-ret));
|
||||
}
|
||||
|
||||
let uinput = unsafe { uinput.assume_init() };
|
||||
let uinput = NonNull::new(uinput).unwrap();
|
||||
|
||||
Ok(Self { file, uinput })
|
||||
pub(crate) async fn from_evdev(evdev: &Evdev) -> Result<Self, Error> {
|
||||
Ok(Self {
|
||||
uinput: Uinput::from_evdev(evdev).await?,
|
||||
})
|
||||
}
|
||||
|
||||
pub(crate) async fn write_raw(
|
||||
|
@ -100,7 +76,7 @@ impl Writer {
|
|||
value: i32,
|
||||
) -> Result<(), Error> {
|
||||
loop {
|
||||
let result = self.file.writable().await?.try_io(|_| {
|
||||
let result = self.uinput.file().writable().await?.try_io(|_| {
|
||||
let ret = unsafe {
|
||||
glue::libevdev_uinput_write_event(
|
||||
self.uinput.as_ptr(),
|
||||
|
@ -125,25 +101,13 @@ impl Writer {
|
|||
}
|
||||
}
|
||||
|
||||
impl Drop for Writer {
|
||||
fn drop(&mut self) {
|
||||
unsafe {
|
||||
glue::libevdev_uinput_destroy(self.uinput.as_ptr());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl Send for Writer {}
|
||||
|
||||
pub struct WriterBuilder {
|
||||
evdev: NonNull<libevdev>,
|
||||
evdev: Evdev,
|
||||
}
|
||||
|
||||
impl WriterBuilder {
|
||||
pub fn new() -> Result<Self, Error> {
|
||||
let evdev = unsafe { glue::libevdev_new() };
|
||||
let evdev = NonNull::new(evdev)
|
||||
.ok_or_else(|| Error::new(ErrorKind::Other, "Failed to create device"))?;
|
||||
let evdev = Evdev::new()?;
|
||||
|
||||
unsafe {
|
||||
glue::libevdev_set_id_bustype(evdev.as_ptr(), glue::BUS_VIRTUAL as _);
|
||||
|
@ -269,16 +233,6 @@ impl WriterBuilder {
|
|||
}
|
||||
|
||||
pub async fn build(self) -> Result<Writer, Error> {
|
||||
task::spawn_blocking(move || unsafe { Writer::from_evdev(self.evdev.as_ref()) }).await?
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl Send for WriterBuilder {}
|
||||
|
||||
impl Drop for WriterBuilder {
|
||||
fn drop(&mut self) {
|
||||
unsafe {
|
||||
glue::libevdev_free(self.evdev.as_ptr());
|
||||
}
|
||||
Writer::from_evdev(&self.evdev).await
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue