use crate::compat::{input_event, input_id, uinput_abs_setup, uinput_setup, UINPUT_MAX_NAME_SIZE};
use crate::ff::FFEffectData;
use crate::inputid::{BusType, InputId};
use crate::{
sys, AttributeSetRef, FFEffectCode, InputEvent, KeyCode, MiscCode, PropType, RelativeAxisCode,
SwitchCode, SynchronizationEvent, UInputCode, UInputEvent, UinputAbsSetup,
};
use std::ffi::{CStr, OsStr};
use std::os::fd::{AsFd, AsRawFd, BorrowedFd, OwnedFd, RawFd};
use std::os::unix::ffi::OsStrExt;
use std::path::{Path, PathBuf};
use std::{fs, io};
const UINPUT_PATH: &str = "/dev/uinput";
const SYSFS_PATH: &str = "/sys/devices/virtual/input";
const DEV_PATH: &str = "/dev/input";
#[derive(Debug)]
pub struct VirtualDeviceBuilder<'a> {
fd: OwnedFd,
name: &'a [u8],
id: Option<input_id>,
ff_effects_max: u32,
}
impl<'a> VirtualDeviceBuilder<'a> {
#[deprecated(note = "use `VirtualDevice::builder()` instead")]
#[doc(hidden)]
pub fn new() -> io::Result<Self> {
let fd = fs::OpenOptions::new()
.read(true)
.write(true)
.open(UINPUT_PATH)?;
Ok(VirtualDeviceBuilder {
fd: fd.into(),
name: Default::default(),
id: None,
ff_effects_max: 0,
})
}
#[inline]
pub fn name<S: AsRef<[u8]> + ?Sized>(mut self, name: &'a S) -> Self {
self.name = name.as_ref();
self
}
#[inline]
pub fn input_id(mut self, id: InputId) -> Self {
self.id = Some(id.0);
self
}
pub fn with_phys(self, path: &CStr) -> io::Result<Self> {
unsafe {
sys::ui_set_phys(self.fd.as_raw_fd(), path.as_ptr())?;
}
Ok(self)
}
pub fn with_keys(self, keys: &AttributeSetRef<KeyCode>) -> io::Result<Self> {
unsafe {
sys::ui_set_evbit(
self.fd.as_raw_fd(),
crate::EventType::KEY.0 as nix::sys::ioctl::ioctl_param_type,
)?;
}
for bit in keys.iter() {
unsafe {
sys::ui_set_keybit(
self.fd.as_raw_fd(),
bit.0 as nix::sys::ioctl::ioctl_param_type,
)?;
}
}
Ok(self)
}
pub fn with_absolute_axis(self, axis: &UinputAbsSetup) -> io::Result<Self> {
unsafe {
sys::ui_set_evbit(
self.fd.as_raw_fd(),
crate::EventType::ABSOLUTE.0 as nix::sys::ioctl::ioctl_param_type,
)?;
sys::ui_set_absbit(
self.fd.as_raw_fd(),
axis.code() as nix::sys::ioctl::ioctl_param_type,
)?;
sys::ui_abs_setup(self.fd.as_raw_fd(), &axis.0 as *const uinput_abs_setup)?;
}
Ok(self)
}
pub fn with_relative_axes(self, axes: &AttributeSetRef<RelativeAxisCode>) -> io::Result<Self> {
unsafe {
sys::ui_set_evbit(
self.fd.as_raw_fd(),
crate::EventType::RELATIVE.0 as nix::sys::ioctl::ioctl_param_type,
)?;
}
for bit in axes.iter() {
unsafe {
sys::ui_set_relbit(
self.fd.as_raw_fd(),
bit.0 as nix::sys::ioctl::ioctl_param_type,
)?;
}
}
Ok(self)
}
pub fn with_properties(self, switches: &AttributeSetRef<PropType>) -> io::Result<Self> {
for bit in switches.iter() {
unsafe {
sys::ui_set_propbit(
self.fd.as_raw_fd(),
bit.0 as nix::sys::ioctl::ioctl_param_type,
)?;
}
}
Ok(self)
}
pub fn with_switches(self, switches: &AttributeSetRef<SwitchCode>) -> io::Result<Self> {
unsafe {
sys::ui_set_evbit(
self.fd.as_raw_fd(),
crate::EventType::SWITCH.0 as nix::sys::ioctl::ioctl_param_type,
)?;
}
for bit in switches.iter() {
unsafe {
sys::ui_set_swbit(
self.fd.as_raw_fd(),
bit.0 as nix::sys::ioctl::ioctl_param_type,
)?;
}
}
Ok(self)
}
pub fn with_ff(self, ff: &AttributeSetRef<FFEffectCode>) -> io::Result<Self> {
unsafe {
sys::ui_set_evbit(
self.fd.as_raw_fd(),
crate::EventType::FORCEFEEDBACK.0 as nix::sys::ioctl::ioctl_param_type,
)?;
}
for bit in ff.iter() {
unsafe {
sys::ui_set_ffbit(
self.fd.as_raw_fd(),
bit.0 as nix::sys::ioctl::ioctl_param_type,
)?;
}
}
Ok(self)
}
pub fn with_ff_effects_max(mut self, ff_effects_max: u32) -> Self {
self.ff_effects_max = ff_effects_max;
self
}
pub fn with_msc(self, misc_set: &AttributeSetRef<MiscCode>) -> io::Result<Self> {
unsafe {
sys::ui_set_evbit(
self.fd.as_raw_fd(),
crate::EventType::MISC.0 as nix::sys::ioctl::ioctl_param_type,
)?;
}
for bit in misc_set.iter() {
unsafe {
sys::ui_set_mscbit(
self.fd.as_raw_fd(),
bit.0 as nix::sys::ioctl::ioctl_param_type,
)?;
}
}
Ok(self)
}
pub fn build(self) -> io::Result<VirtualDevice> {
let mut usetup = uinput_setup {
id: self.id.unwrap_or(DEFAULT_ID),
name: [0; UINPUT_MAX_NAME_SIZE],
ff_effects_max: self.ff_effects_max,
};
let name_bytes = unsafe { &*(self.name as *const [u8] as *const [libc::c_char]) };
assert!(name_bytes.len() + 1 < UINPUT_MAX_NAME_SIZE);
usetup.name[..name_bytes.len()].copy_from_slice(name_bytes);
VirtualDevice::new(self.fd, &usetup)
}
}
const DEFAULT_ID: input_id = input_id {
bustype: BusType::BUS_USB.0,
vendor: 0x1234,
product: 0x5678,
version: 0x111,
};
#[derive(Debug)]
pub struct VirtualDevice {
fd: OwnedFd,
pub(crate) event_buf: Vec<input_event>,
}
impl VirtualDevice {
pub fn builder<'a>() -> io::Result<VirtualDeviceBuilder<'a>> {
#[allow(deprecated)]
VirtualDeviceBuilder::new()
}
fn new(fd: OwnedFd, usetup: &uinput_setup) -> io::Result<Self> {
unsafe { sys::ui_dev_setup(fd.as_raw_fd(), usetup)? };
unsafe { sys::ui_dev_create(fd.as_raw_fd())? };
Ok(VirtualDevice {
fd,
event_buf: vec![],
})
}
#[inline]
fn write_raw(&mut self, events: &[InputEvent]) -> io::Result<()> {
crate::write_events(self.fd.as_fd(), events)?;
Ok(())
}
pub fn get_syspath(&mut self) -> io::Result<PathBuf> {
let mut syspath = vec![0u8; 256];
let len = unsafe { sys::ui_get_sysname(self.fd.as_raw_fd(), &mut syspath)? };
syspath.truncate(len as usize - 1);
let syspath = OsStr::from_bytes(&syspath);
Ok(Path::new(SYSFS_PATH).join(syspath))
}
pub fn enumerate_dev_nodes_blocking(&mut self) -> io::Result<DevNodesBlocking> {
let path = self.get_syspath()?;
let dir = std::fs::read_dir(path)?;
Ok(DevNodesBlocking { dir })
}
#[cfg(feature = "tokio")]
pub async fn enumerate_dev_nodes(&mut self) -> io::Result<DevNodes> {
let path = self.get_syspath()?;
let dir = tokio::fs::read_dir(path).await?;
Ok(DevNodes { dir })
}
pub fn emit(&mut self, events: &[InputEvent]) -> io::Result<()> {
self.write_raw(events)?;
let syn = *SynchronizationEvent::new(crate::SynchronizationCode::SYN_REPORT, 0);
self.write_raw(&[syn])
}
pub fn process_ff_upload(&mut self, event: UInputEvent) -> io::Result<FFUploadEvent> {
assert_eq!(event.code(), UInputCode::UI_FF_UPLOAD);
let mut request: sys::uinput_ff_upload = unsafe { std::mem::zeroed() };
request.request_id = event.value() as u32;
unsafe { sys::ui_begin_ff_upload(self.fd.as_raw_fd(), &mut request)? };
request.retval = 0;
let fd = self.fd.try_clone()?;
Ok(FFUploadEvent { fd, request })
}
pub fn process_ff_erase(&mut self, event: UInputEvent) -> io::Result<FFEraseEvent> {
assert_eq!(event.code(), UInputCode::UI_FF_ERASE);
let mut request: sys::uinput_ff_erase = unsafe { std::mem::zeroed() };
request.request_id = event.value() as u32;
unsafe { sys::ui_begin_ff_erase(self.fd.as_raw_fd(), &mut request)? };
request.retval = 0;
let fd = self.fd.try_clone()?;
Ok(FFEraseEvent { fd, request })
}
pub(crate) fn fill_events(&mut self) -> io::Result<usize> {
let fd = self.fd.as_raw_fd();
self.event_buf.reserve(crate::EVENT_BATCH_SIZE);
let spare_capacity = self.event_buf.spare_capacity_mut();
let spare_capacity_size = std::mem::size_of_val(spare_capacity);
let res = unsafe { libc::read(fd, spare_capacity.as_mut_ptr() as _, spare_capacity_size) };
let bytes_read = nix::errno::Errno::result(res)?;
let num_read = bytes_read as usize / std::mem::size_of::<input_event>();
unsafe {
let len = self.event_buf.len();
self.event_buf.set_len(len + num_read);
}
Ok(num_read)
}
pub fn fetch_events(&mut self) -> io::Result<impl Iterator<Item = InputEvent> + '_> {
self.fill_events()?;
Ok(self.event_buf.drain(..).map(InputEvent::from))
}
#[cfg(feature = "tokio")]
#[inline]
pub fn into_event_stream(self) -> io::Result<VirtualEventStream> {
VirtualEventStream::new(self)
}
}
pub struct DevNodesBlocking {
dir: std::fs::ReadDir,
}
impl Iterator for DevNodesBlocking {
type Item = io::Result<PathBuf>;
fn next(&mut self) -> Option<Self::Item> {
for entry in self.dir.by_ref() {
let entry = match entry {
Ok(entry) => entry,
Err(e) => return Some(Err(e)),
};
let file_name = entry.file_name();
if !file_name.as_bytes().starts_with(b"event") {
continue;
}
let path = Path::new(DEV_PATH).join(file_name);
return Some(Ok(path));
}
None
}
}
#[cfg(feature = "tokio")]
pub struct DevNodes {
dir: tokio::fs::ReadDir,
}
#[cfg(feature = "tokio")]
impl DevNodes {
pub async fn next_entry(&mut self) -> io::Result<Option<PathBuf>> {
while let Some(entry) = self.dir.next_entry().await? {
let file_name = entry.file_name();
if !file_name.as_bytes().starts_with(b"event") {
continue;
}
let path = Path::new(DEV_PATH).join(file_name);
return Ok(Some(path));
}
Ok(None)
}
}
impl AsFd for VirtualDevice {
fn as_fd(&self) -> BorrowedFd<'_> {
self.fd.as_fd()
}
}
impl AsRawFd for VirtualDevice {
fn as_raw_fd(&self) -> RawFd {
self.fd.as_raw_fd()
}
}
pub struct FFUploadEvent {
fd: OwnedFd,
request: sys::uinput_ff_upload,
}
impl FFUploadEvent {
pub fn old_effect(&self) -> FFEffectData {
self.request.old.into()
}
pub fn effect_id(&self) -> i16 {
self.request.effect.id
}
pub fn set_effect_id(&mut self, id: i16) {
self.request.effect.id = id;
}
pub fn effect(&self) -> FFEffectData {
self.request.effect.into()
}
pub fn retval(&self) -> i32 {
self.request.retval
}
pub fn set_retval(&mut self, value: i32) {
self.request.retval = value;
}
}
impl Drop for FFUploadEvent {
fn drop(&mut self) {
unsafe {
let _ = sys::ui_end_ff_upload(self.fd.as_raw_fd(), &self.request);
}
}
}
pub struct FFEraseEvent {
fd: OwnedFd,
request: sys::uinput_ff_erase,
}
impl FFEraseEvent {
pub fn effect_id(&self) -> u32 {
self.request.effect_id
}
pub fn retval(&self) -> i32 {
self.request.retval
}
pub fn set_retval(&mut self, value: i32) {
self.request.retval = value;
}
}
impl Drop for FFEraseEvent {
fn drop(&mut self) {
unsafe {
let _ = sys::ui_end_ff_erase(self.fd.as_raw_fd(), &self.request);
}
}
}
#[cfg(feature = "tokio")]
mod tokio_stream {
use super::*;
use std::future::poll_fn;
use std::task::{ready, Context, Poll};
use tokio::io::unix::AsyncFd;
pub struct VirtualEventStream {
device: AsyncFd<VirtualDevice>,
index: usize,
}
impl Unpin for VirtualEventStream {}
impl VirtualEventStream {
pub(crate) fn new(device: VirtualDevice) -> io::Result<Self> {
use nix::fcntl;
fcntl::fcntl(device.as_raw_fd(), fcntl::F_SETFL(fcntl::OFlag::O_NONBLOCK))?;
let device = AsyncFd::new(device)?;
Ok(Self { device, index: 0 })
}
pub fn device(&self) -> &VirtualDevice {
self.device.get_ref()
}
pub fn device_mut(&mut self) -> &mut VirtualDevice {
self.device.get_mut()
}
pub async fn next_event(&mut self) -> io::Result<InputEvent> {
poll_fn(|cx| self.poll_event(cx)).await
}
pub fn poll_event(&mut self, cx: &mut Context<'_>) -> Poll<io::Result<InputEvent>> {
'outer: loop {
if let Some(&ev) = self.device.get_ref().event_buf.get(self.index) {
self.index += 1;
return Poll::Ready(Ok(InputEvent::from(ev)));
}
self.device.get_mut().event_buf.clear();
self.index = 0;
loop {
let mut guard = ready!(self.device.poll_read_ready_mut(cx))?;
let res = guard.try_io(|device| device.get_mut().fill_events());
match res {
Ok(res) => {
let _ = res?;
continue 'outer;
}
Err(_would_block) => continue,
}
}
}
}
}
#[cfg(feature = "stream-trait")]
impl futures_core::Stream for VirtualEventStream {
type Item = io::Result<InputEvent>;
fn poll_next(
self: std::pin::Pin<&mut Self>,
cx: &mut Context<'_>,
) -> Poll<Option<Self::Item>> {
self.get_mut().poll_event(cx).map(Some)
}
}
}
#[cfg(feature = "tokio")]
pub use tokio_stream::VirtualEventStream;