mirror of
https://github.com/htrefil/rkvm.git
synced 2024-11-17 07:47:45 +01:00
Implement libevdev event management
This commit is contained in:
parent
68fa721095
commit
ed7d5bcf06
4 changed files with 202 additions and 64 deletions
|
@ -6,8 +6,10 @@ fn main() {
|
|||
println!("cargo:rerun-if-changed=glue/glue.h");
|
||||
println!("cargo:rustc-link-lib=evdev");
|
||||
|
||||
// TODO: pkg-config
|
||||
let bindings = Builder::default()
|
||||
.header("glue/glue.h")
|
||||
.clang_arg("-I/usr/include/libevdev-1.0/")
|
||||
.parse_callbacks(Box::new(CargoCallbacks))
|
||||
.generate()
|
||||
.unwrap();
|
||||
|
|
|
@ -1 +1,2 @@
|
|||
#include <libevdev-1.0/libevdev/libevdev.h>
|
||||
#include <libevdev/libevdev.h>
|
||||
#include <libevdev/libevdev-uinput.h>
|
|
@ -1,7 +1,5 @@
|
|||
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;
|
||||
|
@ -14,7 +12,7 @@ use std::task::{Context, Poll};
|
|||
use tokio::io::Registration;
|
||||
|
||||
pub(crate) struct EventReader {
|
||||
file: EventedFile,
|
||||
file: File,
|
||||
registration: Registration,
|
||||
evdev: *mut libevdev,
|
||||
}
|
||||
|
@ -22,33 +20,29 @@ pub(crate) struct EventReader {
|
|||
impl EventReader {
|
||||
pub async fn new(path: &Path) -> Result<Self, Error> {
|
||||
let path = path.to_owned();
|
||||
|
||||
tokio::task::spawn_blocking(move || Self::open_sync(&path)).await?
|
||||
tokio::task::spawn_blocking(move || Self::new_sync(&path)).await?
|
||||
}
|
||||
|
||||
pub async fn read(&mut self) -> Result<input_event, Error> {
|
||||
Read { reader: self }.await
|
||||
}
|
||||
|
||||
fn open_sync(path: &Path) -> Result<Self, Error> {
|
||||
fn new_sync(path: &Path) -> Result<Self, Error> {
|
||||
let file = OpenOptions::new()
|
||||
.read(true)
|
||||
.custom_flags(libc::O_NONBLOCK)
|
||||
.open(path)
|
||||
.map(|file| EventedFile { file })?;
|
||||
let registration = Registration::new(&file)?;
|
||||
.open(path)?;
|
||||
let registration = Registration::new(&EventedFd(&file.as_raw_fd()))?;
|
||||
|
||||
let mut evdev = std::ptr::null_mut();
|
||||
|
||||
let ret =
|
||||
unsafe { glue::libevdev_new_from_fd(file.file.as_raw_fd(), &mut evdev as *mut _) };
|
||||
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));
|
||||
}
|
||||
|
||||
let evdev = unsafe { evdev.assume_init() };
|
||||
let ret = unsafe { glue::libevdev_grab(evdev, glue::libevdev_grab_mode_LIBEVDEV_GRAB) };
|
||||
if ret < 0 {
|
||||
unsafe { glue::libevdev_free(evdev) };
|
||||
unsafe {
|
||||
glue::libevdev_free(evdev);
|
||||
}
|
||||
|
||||
return Err(Error::from_raw_os_error(-ret));
|
||||
}
|
||||
|
||||
|
@ -58,56 +52,39 @@ impl EventReader {
|
|||
evdev,
|
||||
})
|
||||
}
|
||||
|
||||
pub async fn read(&mut self) -> Result<input_event, Error> {
|
||||
Read {
|
||||
reader: self,
|
||||
polling: false,
|
||||
}
|
||||
.await
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for EventReader {
|
||||
fn drop(&mut self) {
|
||||
unsafe {
|
||||
glue::libevdev_free(self.evdev);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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,
|
||||
polling: bool,
|
||||
}
|
||||
|
||||
impl Future for Read<'_> {
|
||||
type Output = Result<input_event, Error>;
|
||||
|
||||
fn poll(self: Pin<&mut Self>, context: &mut Context) -> Poll<Self::Output> {
|
||||
if let Poll::Pending = self.reader.registration.poll_read_ready(context)? {
|
||||
return Poll::Pending;
|
||||
fn poll(mut self: Pin<&mut Self>, context: &mut Context) -> Poll<Self::Output> {
|
||||
if self.polling {
|
||||
if let Poll::Pending = self.reader.registration.poll_read_ready(context)? {
|
||||
return Poll::Pending;
|
||||
}
|
||||
}
|
||||
|
||||
let mut event = MaybeUninit::uninit();
|
||||
|
@ -118,10 +95,19 @@ impl Future for Read<'_> {
|
|||
event.as_mut_ptr(),
|
||||
)
|
||||
};
|
||||
|
||||
if !self.polling && ret == -libc::EAGAIN {
|
||||
self.polling = true;
|
||||
return self.poll(context);
|
||||
}
|
||||
|
||||
self.polling = false;
|
||||
|
||||
if ret < 0 {
|
||||
return Poll::Ready(Err(Error::from_raw_os_error(-ret)));
|
||||
}
|
||||
|
||||
Poll::Ready(Ok(unsafe { event.assume_init() }))
|
||||
let event = unsafe { event.assume_init() };
|
||||
Poll::Ready(Ok(event))
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,12 +1,75 @@
|
|||
use crate::event::Event;
|
||||
use crate::glue::input_event;
|
||||
use std::io::Error;
|
||||
use crate::glue::{self, input_event, libevdev, libevdev_uinput};
|
||||
use mio::unix::EventedFd;
|
||||
use std::fs::OpenOptions;
|
||||
use std::future::Future;
|
||||
use std::io::{Error, ErrorKind};
|
||||
use std::mem::MaybeUninit;
|
||||
use std::ops::RangeInclusive;
|
||||
use std::os::unix::fs::OpenOptionsExt;
|
||||
use std::os::unix::io::AsRawFd;
|
||||
use std::pin::Pin;
|
||||
use std::task::{Context, Poll};
|
||||
use tokio::io::Registration;
|
||||
|
||||
pub struct EventWriter {}
|
||||
pub struct EventWriter {
|
||||
evdev: *mut libevdev,
|
||||
uinput: *mut libevdev_uinput,
|
||||
registration: Registration,
|
||||
}
|
||||
|
||||
impl EventWriter {
|
||||
pub async fn new() -> Result<Self, Error> {
|
||||
todo!()
|
||||
tokio::task::spawn_blocking(|| Self::new_sync()).await?
|
||||
}
|
||||
|
||||
fn new_sync() -> Result<Self, Error> {
|
||||
let evdev = unsafe { glue::libevdev_new() };
|
||||
if evdev.is_null() {
|
||||
return Err(Error::new(ErrorKind::Other, "Failed to create device"));
|
||||
}
|
||||
|
||||
if let Err(err) = unsafe { setup_evdev(evdev) } {
|
||||
unsafe {
|
||||
glue::libevdev_free(evdev);
|
||||
}
|
||||
|
||||
return Err(err);
|
||||
}
|
||||
|
||||
let mut uinput = MaybeUninit::uninit();
|
||||
let ret = unsafe {
|
||||
glue::libevdev_uinput_create_from_device(
|
||||
evdev,
|
||||
glue::libevdev_uinput_open_mode_LIBEVDEV_UINPUT_OPEN_MANAGED,
|
||||
uinput.as_mut_ptr(),
|
||||
)
|
||||
};
|
||||
|
||||
if ret < 0 {
|
||||
unsafe { glue::libevdev_free(evdev) };
|
||||
return Err(Error::from_raw_os_error(-ret));
|
||||
}
|
||||
|
||||
let uinput = unsafe { uinput.assume_init() };
|
||||
let fd = unsafe { glue::libevdev_uinput_get_fd(uinput) };
|
||||
let registration = match Registration::new(&EventedFd(&fd)) {
|
||||
Ok(registration) => registration,
|
||||
Err(err) => {
|
||||
unsafe {
|
||||
glue::libevdev_uinput_destroy(uinput);
|
||||
glue::libevdev_free(evdev);
|
||||
};
|
||||
|
||||
return Err(err);
|
||||
}
|
||||
};
|
||||
|
||||
Ok(Self {
|
||||
evdev,
|
||||
uinput,
|
||||
registration,
|
||||
})
|
||||
}
|
||||
|
||||
pub async fn write(&mut self, event: Event) -> Result<(), Error> {
|
||||
|
@ -14,6 +77,92 @@ impl EventWriter {
|
|||
}
|
||||
|
||||
pub(crate) async fn write_raw(&mut self, event: input_event) -> Result<(), Error> {
|
||||
todo!()
|
||||
WriteRaw {
|
||||
writer: self,
|
||||
event,
|
||||
polling: false,
|
||||
}
|
||||
.await
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for EventWriter {
|
||||
fn drop(&mut self) {
|
||||
unsafe {
|
||||
glue::libevdev_uinput_destroy(self.uinput);
|
||||
glue::libevdev_free(self.evdev);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl Send for EventWriter {}
|
||||
|
||||
struct WriteRaw<'a> {
|
||||
writer: &'a mut EventWriter,
|
||||
event: input_event,
|
||||
polling: bool,
|
||||
}
|
||||
|
||||
impl Future for WriteRaw<'_> {
|
||||
type Output = Result<(), Error>;
|
||||
|
||||
fn poll(mut self: Pin<&mut Self>, context: &mut Context) -> Poll<Self::Output> {
|
||||
if self.polling {
|
||||
if let Poll::Pending = self.writer.registration.poll_write_ready(context)? {
|
||||
return Poll::Pending;
|
||||
}
|
||||
}
|
||||
|
||||
let ret = unsafe {
|
||||
glue::libevdev_uinput_write_event(
|
||||
self.writer.uinput as *const _,
|
||||
self.event.type_ as _,
|
||||
self.event.code as _,
|
||||
self.event.value,
|
||||
)
|
||||
};
|
||||
|
||||
if !self.polling && ret == -libc::EAGAIN {
|
||||
self.polling = true;
|
||||
return self.poll(context);
|
||||
}
|
||||
|
||||
self.polling = false;
|
||||
|
||||
if ret < 0 {
|
||||
return Poll::Ready(Err(Error::from_raw_os_error(-ret)));
|
||||
}
|
||||
|
||||
Poll::Ready(Ok(()))
|
||||
}
|
||||
}
|
||||
|
||||
const TYPES: &[(u32, &[RangeInclusive<u32>])] = &[
|
||||
(glue::EV_SYN, &[glue::SYN_REPORT..=glue::SYN_REPORT]),
|
||||
(glue::EV_REL, &[0..=glue::REL_MAX]),
|
||||
(glue::EV_KEY, &[0..=glue::KEY_MAX]),
|
||||
];
|
||||
|
||||
unsafe fn setup_evdev(evdev: *mut libevdev) -> Result<(), Error> {
|
||||
glue::libevdev_set_name(evdev, b"rkvm\0".as_ptr() as *const _);
|
||||
glue::libevdev_set_id_product(evdev, 1);
|
||||
glue::libevdev_set_id_version(evdev, 1);
|
||||
glue::libevdev_set_id_vendor(evdev, i32::from_be_bytes(*b"rkvm"));
|
||||
glue::libevdev_set_id_bustype(evdev, glue::BUS_USB as _);
|
||||
|
||||
for (r#type, codes) in TYPES.iter().copied() {
|
||||
let ret = glue::libevdev_enable_event_type(evdev, r#type);
|
||||
if ret < 0 {
|
||||
return Err(Error::from_raw_os_error(-ret));
|
||||
}
|
||||
|
||||
for code in codes.iter().cloned().flat_map(|code| code) {
|
||||
let ret = glue::libevdev_enable_event_code(evdev, r#type, code, std::ptr::null_mut());
|
||||
if ret < 0 {
|
||||
return Err(Error::from_raw_os_error(-ret));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue