Skip to content

Commit

Permalink
Asyncify uinput writing, don't write empty EV_SYNs
Browse files Browse the repository at this point in the history
  • Loading branch information
htrefil committed Apr 19, 2023
1 parent 98431d4 commit a9d5155
Show file tree
Hide file tree
Showing 2 changed files with 212 additions and 123 deletions.
236 changes: 142 additions & 94 deletions rkvm-input/src/event_reader.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,15 @@ use std::mem::MaybeUninit;
use std::os::unix::fs::OpenOptionsExt;
use std::os::unix::io::AsRawFd;
use std::path::Path;
use std::ptr::NonNull;
use tokio::io::unix::AsyncFd;
use tokio::task;

pub(crate) struct EventReader {
file: AsyncFd<File>,
evdev: *mut libevdev,
uinput: *mut libevdev_uinput,
evdev_file: AsyncFd<File>,
evdev_handle: NonNull<libevdev>,
uinput_file: AsyncFd<File>,
uinput_handle: NonNull<libevdev_uinput>,
}

impl EventReader {
Expand All @@ -28,25 +30,30 @@ impl EventReader {
}

fn open_sync(path: &Path) -> Result<Self, OpenError> {
let file = OpenOptions::new()
let evdev_file = OpenOptions::new()
.read(true)
.custom_flags(libc::O_NONBLOCK)
.open(path)
.and_then(AsyncFd::new)?;

let mut evdev = MaybeUninit::uninit();
let mut evdev_handle = MaybeUninit::uninit();

let ret = unsafe {
glue::libevdev_new_from_fd(evdev_file.as_raw_fd(), evdev_handle.as_mut_ptr())
};

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_handle = unsafe { evdev_handle.assume_init() };
let evdev_handle = NonNull::new(evdev_handle).unwrap();

let (vendor, product, version) = unsafe {
(
glue::libevdev_get_id_vendor(evdev),
glue::libevdev_get_id_product(evdev),
glue::libevdev_get_id_version(evdev),
glue::libevdev_get_id_vendor(evdev_handle.as_ptr()),
glue::libevdev_get_id_product(evdev_handle.as_ptr()),
glue::libevdev_get_id_version(evdev_handle.as_ptr()),
)
};

Expand All @@ -59,120 +66,154 @@ impl EventReader {
// the current switch state. This ensures that the device, kernel, and userspace
// state is in sync."
// We have no way of knowing that.
let has_sw = unsafe { glue::libevdev_has_event_type(evdev, glue::EV_SW) == 1 };
let has_sw =
unsafe { glue::libevdev_has_event_type(evdev_handle.as_ptr(), glue::EV_SW) == 1 };

if vendor || has_sw {
unsafe {
glue::libevdev_free(evdev);
glue::libevdev_free(evdev_handle.as_ptr());
}

return Err(OpenError::NotAppliable);
}

unsafe {
glue::libevdev_set_id_vendor(evdev, device_id::VENDOR as _);
glue::libevdev_set_id_product(evdev, device_id::PRODUCT as _);
glue::libevdev_set_id_version(evdev, device_id::VERSION as _);
glue::libevdev_set_id_vendor(evdev_handle.as_ptr(), device_id::VENDOR as _);
glue::libevdev_set_id_product(evdev_handle.as_ptr(), device_id::PRODUCT as _);
glue::libevdev_set_id_version(evdev_handle.as_ptr(), device_id::VERSION as _);
}

let ret = unsafe { glue::libevdev_grab(evdev, glue::libevdev_grab_mode_LIBEVDEV_GRAB) };
let ret = unsafe {
glue::libevdev_grab(
evdev_handle.as_ptr(),
glue::libevdev_grab_mode_LIBEVDEV_GRAB,
)
};

if ret < 0 {
unsafe {
glue::libevdev_free(evdev);
glue::libevdev_free(evdev_handle.as_ptr());
}

return Err(Error::from_raw_os_error(-ret).into());
}

let mut uinput = MaybeUninit::uninit();
// libevdev opens /dev/uinput with O_RDWR.
let uinput_file = OpenOptions::new()
.read(true)
.write(true)
.open("/dev/uinput")
.and_then(AsyncFd::new)?;

let mut uinput_handle = 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(),
evdev_handle.as_ptr(),
uinput_file.as_raw_fd(),
uinput_handle.as_mut_ptr(),
)
};

if ret < 0 {
unsafe {
glue::libevdev_free(evdev);
glue::libevdev_free(evdev_handle.as_ptr());
}

return Err(Error::from_raw_os_error(-ret).into());
}

let uinput_handle = unsafe { uinput_handle.assume_init() };
let uinput_handle = NonNull::new(uinput_handle).unwrap();

Ok(Self {
file,
evdev,
uinput: unsafe { uinput.assume_init() },
evdev_file,
evdev_handle,
uinput_file,
uinput_handle,
})
}

pub async fn read(&mut self) -> Result<EventPack, Error> {
let mut events = EventPack::new();

loop {
let raw = self.read_raw().await?;
let event = match (raw.type_ as _, raw.code as _, raw.value) {
// These should not be propagated, it will result in double scrolling otherwise.
(glue::EV_REL, glue::REL_HWHEEL | glue::REL_WHEEL, _) => continue,
(glue::EV_REL, glue::REL_HWHEEL_HI_RES, value) => Some(Event::MouseScroll {
axis: Axis::X,
delta: value,
}),
(glue::EV_REL, glue::REL_WHEEL_HI_RES, value) => Some(Event::MouseScroll {
axis: Axis::Y,
delta: value,
}),
(glue::EV_REL, glue::REL_X, value) => Some(Event::MouseMove {
axis: Axis::X,
delta: value,
}),
(glue::EV_REL, glue::REL_Y, value) => Some(Event::MouseMove {
axis: Axis::Y,
delta: value,
}),
(glue::EV_KEY, code, 0) => KeyKind::from_raw(code as _).map(|kind| Event::Key {
direction: Direction::Up,
kind,
}),
(glue::EV_KEY, code, 1) => KeyKind::from_raw(code as _).map(|kind| Event::Key {
direction: Direction::Down,
kind,
}),
(glue::EV_SYN, glue::SYN_REPORT, _) => break,
_ => None,
};

if let Some(event) = event {
events.push(event);
continue;
let mut events = EventPack::new();
let mut wrote = false;

loop {
let raw = self.read_raw().await?;
let event = match (raw.type_ as _, raw.code as _, raw.value) {
// These should not be propagated, it will result in double scrolling otherwise.
(glue::EV_REL, glue::REL_HWHEEL | glue::REL_WHEEL, _) => continue,
(glue::EV_REL, glue::REL_HWHEEL_HI_RES, value) => Some(Event::MouseScroll {
axis: Axis::X,
delta: value,
}),
(glue::EV_REL, glue::REL_WHEEL_HI_RES, value) => Some(Event::MouseScroll {
axis: Axis::Y,
delta: value,
}),
(glue::EV_REL, glue::REL_X, value) => Some(Event::MouseMove {
axis: Axis::X,
delta: value,
}),
(glue::EV_REL, glue::REL_Y, value) => Some(Event::MouseMove {
axis: Axis::Y,
delta: value,
}),
(glue::EV_KEY, code, 0) => {
KeyKind::from_raw(code as _).map(|kind| Event::Key {
direction: Direction::Up,
kind,
})
}
(glue::EV_KEY, code, 1) => {
KeyKind::from_raw(code as _).map(|kind| Event::Key {
direction: Direction::Down,
kind,
})
}
(glue::EV_SYN, glue::SYN_REPORT, _) => break,
_ => None,
};

if let Some(event) = event {
events.push(event);
continue;
}

self.write_raw(&raw).await?;
wrote = true;
}

self.write_raw(&raw).await?;
}
// Send an EV_SYN only if we actually wrote something back.
if wrote {
self.write_raw(&input_event {
type_: glue::EV_SYN as _,
code: glue::SYN_REPORT as _,
value: 0,
time: timeval {
tv_sec: 0,
tv_usec: 0,
},
})
.await?;
}

self.write_raw(&input_event {
type_: glue::EV_SYN as _,
code: glue::SYN_REPORT as _,
value: 0,
time: timeval {
tv_sec: 0,
tv_usec: 0,
},
})
.await?;
if !events.is_empty() {
return Ok(events);
}

Ok(events)
// At this point, we received an EV_SYN, but no actual events useful to us, so try again.
}
}

async fn read_raw(&mut self) -> Result<input_event, Error> {
loop {
let result = self.file.readable().await?.try_io(|_| {
let result = self.evdev_file.readable().await?.try_io(|_| {
let mut event = MaybeUninit::uninit();
let ret = unsafe {
glue::libevdev_next_event(
self.evdev,
self.evdev_handle.as_ptr(),
glue::libevdev_read_flag_LIBEVDEV_READ_FLAG_NORMAL,
event.as_mut_ptr(),
)
Expand All @@ -187,37 +228,44 @@ impl EventReader {
});

match result {
Ok(Ok(event)) => return Ok(event),
Ok(Err(err)) => return Err(err),
Ok(result) => return result,
Err(_) => continue, // This means it would block.
}
}
}

async fn write_raw(&mut self, event: &input_event) -> Result<(), Error> {
// TODO: This can block.
let ret = unsafe {
glue::libevdev_uinput_write_event(
self.uinput,
event.type_ as _,
event.code as _,
event.value,
)
};
loop {
let result = self.uinput_file.writable().await?.try_io(|_| {
let ret = unsafe {
glue::libevdev_uinput_write_event(
self.uinput_handle.as_ptr(),
event.type_ as _,
event.code as _,
event.value,
)
};

if ret < 0 {
return Err(Error::from_raw_os_error(-ret).into());
}
if ret < 0 {
return Err(Error::from_raw_os_error(-ret).into());
}

Ok(())
});

Ok(())
match result {
Ok(result) => return result,
Err(_) => continue, // This means it would block.
}
}
}
}

impl Drop for EventReader {
fn drop(&mut self) {
unsafe {
glue::libevdev_uinput_destroy(self.uinput);
glue::libevdev_free(self.evdev);
glue::libevdev_uinput_destroy(self.uinput_handle.as_ptr());
glue::libevdev_free(self.evdev_handle.as_ptr());
}
}
}
Expand Down
Loading

0 comments on commit a9d5155

Please sign in to comment.