use std::{io, fs, ptr};
use std::os::unix::io::{RawFd, AsRawFd, IntoRawFd, FromRawFd};
use std::os::unix::ffi::OsStrExt;
use std::os::fd::{AsFd, BorrowedFd, OwnedFd};
use std::os::raw::c_char;
use std::mem::{MaybeUninit, size_of};
use std::path::{Path, PathBuf};
use std::slice::{from_raw_parts, from_raw_parts_mut};
use std::ffi::{OsStr, OsString, CStr};
use crate::sys;
use nix;
use crate::{Key, InputId, AbsoluteInfoSetup, kinds};
use crate::macros::convert_error;
pub use crate::sys::{UINPUT_MAX_NAME_SIZE, UINPUT_VERSION};
pub struct UInputHandle<F>(F);
fn copy_name(dest: &mut [c_char; UINPUT_MAX_NAME_SIZE as usize], name: &[u8]) -> io::Result<()> {
if name.len() >= UINPUT_MAX_NAME_SIZE as usize {
Err(io::Error::new(io::ErrorKind::InvalidInput, "name too long"))
} else {
unsafe {
ptr::copy_nonoverlapping(name.as_ptr() as *const _, dest.as_mut_ptr() as *mut _, name.len());
}
dest[name.len()] = 0;
Ok(())
}
}
impl<F> UInputHandle<F> {
pub const fn new(fd: F) -> Self {
UInputHandle(fd)
}
pub fn into_inner(self) -> F {
self.0
}
pub const fn as_inner(&self) -> &F {
&self.0
}
pub fn as_inner_mut(&mut self) -> &mut F {
&mut self.0
}
}
impl<F: AsRawFd> AsFd for UInputHandle<F> {
fn as_fd<'a>(&'a self) -> BorrowedFd<'a> {
unsafe {
BorrowedFd::borrow_raw(self.fd())
}
}
}
impl<F: IntoRawFd> IntoRawFd for UInputHandle<F> {
fn into_raw_fd(self) -> RawFd {
self.0.into_raw_fd()
}
}
impl<F: FromRawFd> FromRawFd for UInputHandle<F> {
unsafe fn from_raw_fd(fd: RawFd) -> Self {
UInputHandle(FromRawFd::from_raw_fd(fd))
}
}
impl UInputHandle<OwnedFd> {
pub unsafe fn from_fd(fd: RawFd) -> Self {
FromRawFd::from_raw_fd(fd)
}
}
impl<F: AsRawFd> UInputHandle<F> {
#[inline]
fn fd(&self) -> RawFd {
self.0.as_raw_fd()
}
pub fn create_legacy(&self, id: &InputId, name: &[u8], ff_effects_max: u32, abs: &[AbsoluteInfoSetup]) -> io::Result<()> {
let mut setup: sys::uinput_user_dev = unsafe { MaybeUninit::zeroed().assume_init() };
setup.id = (*id).into();
setup.ff_effects_max = ff_effects_max;
copy_name(&mut setup.name, name)?;
for abs in abs {
let code = abs.axis as usize;
if code >= sys::ABS_CNT as _ {
return Err(io::Error::new(io::ErrorKind::InvalidInput, "invalid abs axis code"))
}
let abs = &abs.info;
setup.absmax[code] = abs.maximum;
setup.absmin[code] = abs.minimum;
setup.absfuzz[code] = abs.fuzz;
setup.absflat[code] = abs.flat;
}
let setup = unsafe { from_raw_parts(&setup as *const _ as *const u8, size_of::<sys::uinput_user_dev>()) };
nix::unistd::write(self, setup).map_err(convert_error)?;
self.dev_create()
}
pub fn create(&self, id: &InputId, name: &[u8], ff_effects_max: u32, abs: &[AbsoluteInfoSetup]) -> io::Result<()> {
let mut setup: sys::uinput_setup = unsafe { MaybeUninit::zeroed().assume_init() };
setup.id = (*id).into();
setup.ff_effects_max = ff_effects_max;
copy_name(&mut setup.name, name)?;
match self.dev_setup(&setup) {
Err(ref e) if e.raw_os_error() == Some(sys::Errno::EINVAL as _) =>
return self.create_legacy(id, name, ff_effects_max, abs),
v => v,
}?;
for abs in abs {
self.abs_setup(abs.into())?;
}
self.dev_create()
}
pub fn write(&self, events: &[sys::input_event]) -> io::Result<usize> {
let events = unsafe { from_raw_parts(events.as_ptr() as *const u8, size_of::<sys::input_event>() * events.len()) };
nix::unistd::write(self, events)
.map(|c| c / size_of::<sys::input_event>()).map_err(convert_error)
}
pub fn read(&self, events: &mut [sys::input_event]) -> io::Result<usize> {
let events = unsafe { from_raw_parts_mut(events.as_mut_ptr() as *mut u8, size_of::<sys::input_event>() * events.len()) };
nix::unistd::read(self.fd(), events)
.map(|len| len / size_of::<sys::input_event>()).map_err(convert_error)
}
pub fn sys_path(&self) -> io::Result<PathBuf> {
let sys = self.sys_name()?;
let sys = CStr::from_bytes_with_nul(&sys).map(|c| c.to_bytes()).unwrap_or(&sys);
Ok(Path::new("/sys/devices/virtual/input/").join(OsStr::from_bytes(sys)))
}
pub fn evdev_name(&self) -> io::Result<OsString> {
let sys = self.sys_path()?;
fs::read_dir(&sys)?.filter_map(|e| match e {
Err(err) => Some(Err(err)),
Ok(e) => match e.file_type() {
Err(err) => Some(Err(err)),
Ok(ty) if ty.is_dir() => {
let name = e.file_name();
if name.as_bytes().starts_with(b"event") {
Some(Ok(e.file_name()))
} else {
None
}
},
Ok(..) => None,
},
}).next().unwrap_or_else(|| Err(io::Error::new(io::ErrorKind::NotFound, "event input device not found")))
}
pub fn evdev_path(&self) -> io::Result<PathBuf> {
self.evdev_name().map(|ev| Path::new("/dev/input/").join(ev))
}
ioctl_impl! {
{
@call dev_create = ui_dev_create
}
{
@call dev_destroy = ui_dev_destroy
}
{
@set dev_setup(&sys::uinput_setup) = ui_dev_setup
}
{
@set abs_setup(&sys::uinput_abs_setup) = ui_abs_setup
}
{
@set set_evbit(kinds::EventKind) = ui_set_evbit
}
{
@set set_keybit(Key) = ui_set_keybit
}
{
@set set_relbit(kinds::RelativeAxis) = ui_set_relbit
}
{
@set set_absbit(kinds::AbsoluteAxis) = ui_set_absbit
}
{
@set set_mscbit(kinds::MiscKind) = ui_set_mscbit
}
{
@set set_ledbit(kinds::LedKind) = ui_set_ledbit
}
{
@set set_sndbit(kinds::SoundKind) = ui_set_sndbit
}
{
@set set_ffbit(kinds::ForceFeedbackKind) = ui_set_ffbit
}
{
@set_str set_phys = ui_set_phys
}
{
@set set_swbit(kinds::SwitchKind) = ui_set_swbit
}
{
@set set_propbit(kinds::InputProperty) = ui_set_propbit
}
{
@set ff_upload_begin(&mut sys::uinput_ff_upload) = ui_begin_ff_upload
}
{
@set ff_upload_end(&sys::uinput_ff_upload) = ui_end_ff_upload
}
{
@set ff_erase_begin(&mut sys::uinput_ff_erase) = ui_begin_ff_erase
}
{
@set ff_erase_end(&sys::uinput_ff_erase) = ui_end_ff_erase
}
{
@get_str sys_name, sys_name_buf = ui_get_sysname
}
{
@get version = ui_get_version -> u32
}
}
}