input_linux/
uinput.rs

1//! An interface to the Linux uinput kernel module that can be used to create
2//! virtual input devices.
3
4use std::{io, fs, ptr};
5use std::os::unix::io::{RawFd, AsRawFd, IntoRawFd, FromRawFd};
6use std::os::unix::ffi::OsStrExt;
7use std::os::fd::{AsFd, BorrowedFd, OwnedFd};
8use std::os::raw::c_char;
9use std::mem::{MaybeUninit, size_of};
10use std::path::{Path, PathBuf};
11use std::slice::{from_raw_parts, from_raw_parts_mut};
12use std::ffi::{OsStr, OsString, CStr};
13use crate::sys;
14use nix;
15use crate::{Key, InputId, AbsoluteInfoSetup, kinds};
16use crate::macros::convert_error;
17
18pub use crate::sys::{UINPUT_MAX_NAME_SIZE, UINPUT_VERSION};
19
20/// A handle to a uinput allowing the use of ioctls
21pub struct UInputHandle<F>(F);
22
23fn copy_name(dest: &mut [c_char; UINPUT_MAX_NAME_SIZE as usize], name: &[u8]) -> io::Result<()> {
24    if name.len() >= UINPUT_MAX_NAME_SIZE as usize {
25        Err(io::Error::new(io::ErrorKind::InvalidInput, "name too long"))
26    } else {
27        unsafe {
28            ptr::copy_nonoverlapping(name.as_ptr() as *const _, dest.as_mut_ptr() as *mut _, name.len());
29        }
30        dest[name.len()] = 0;
31
32        Ok(())
33    }
34}
35
36impl<F> UInputHandle<F> {
37    /// Create a new handle using an existing open file object.
38    pub const fn new(fd: F) -> Self {
39        UInputHandle(fd)
40    }
41
42    /// Extracts the contained handle.
43    pub fn into_inner(self) -> F {
44        self.0
45    }
46
47    /// A reference to the contained handle.
48    pub const fn as_inner(&self) -> &F {
49        &self.0
50    }
51
52    /// A mutable reference to the contained handle.
53    pub fn as_inner_mut(&mut self) -> &mut F {
54        &mut self.0
55    }
56}
57
58impl<F: AsRawFd> AsFd for UInputHandle<F> {
59    fn as_fd<'a>(&'a self) -> BorrowedFd<'a> {
60        unsafe {
61            BorrowedFd::borrow_raw(self.fd())
62        }
63    }
64}
65
66impl<F: IntoRawFd> IntoRawFd for UInputHandle<F> {
67    fn into_raw_fd(self) -> RawFd {
68        self.0.into_raw_fd()
69    }
70}
71
72impl<F: FromRawFd> FromRawFd for UInputHandle<F> {
73    unsafe fn from_raw_fd(fd: RawFd) -> Self {
74        UInputHandle(FromRawFd::from_raw_fd(fd))
75    }
76}
77
78impl UInputHandle<OwnedFd> {
79    /// Create a new handle from a raw file descriptor.
80    pub unsafe fn from_fd(fd: RawFd) -> Self {
81        FromRawFd::from_raw_fd(fd)
82    }
83}
84
85impl<F: AsRawFd> UInputHandle<F> {
86    #[inline]
87    fn fd(&self) -> RawFd {
88        self.0.as_raw_fd()
89    }
90
91    /// Create a new uinput device using the legacy `UI_DEV_CREATE` interface
92    pub fn create_legacy(&self, id: &InputId, name: &[u8], ff_effects_max: u32, abs: &[AbsoluteInfoSetup]) -> io::Result<()> {
93        let mut setup: sys::uinput_user_dev = unsafe { MaybeUninit::zeroed().assume_init() };
94        setup.id = (*id).into();
95        setup.ff_effects_max = ff_effects_max;
96
97        copy_name(&mut setup.name, name)?;
98
99        for abs in abs {
100            let code = abs.axis as usize;
101            if code >= sys::ABS_CNT as _ {
102                return Err(io::Error::new(io::ErrorKind::InvalidInput, "invalid abs axis code"))
103            }
104
105            let abs = &abs.info;
106            setup.absmax[code] = abs.maximum;
107            setup.absmin[code] = abs.minimum;
108            setup.absfuzz[code] = abs.fuzz;
109            setup.absflat[code] = abs.flat;
110        }
111
112        let setup = unsafe { from_raw_parts(&setup as *const _ as *const u8, size_of::<sys::uinput_user_dev>()) };
113        nix::unistd::write(self, setup).map_err(convert_error)?;
114
115        self.dev_create()
116    }
117
118    /// Create a new uinput device, and fall back on the legacy interface if necessary
119    pub fn create(&self, id: &InputId, name: &[u8], ff_effects_max: u32, abs: &[AbsoluteInfoSetup]) -> io::Result<()> {
120        let mut setup: sys::uinput_setup = unsafe { MaybeUninit::zeroed().assume_init() };
121        setup.id = (*id).into();
122        setup.ff_effects_max = ff_effects_max;
123
124        copy_name(&mut setup.name, name)?;
125
126        match self.dev_setup(&setup) {
127            Err(ref e) if e.raw_os_error() == Some(sys::Errno::EINVAL as _) =>
128                return self.create_legacy(id, name, ff_effects_max, abs),
129            v => v,
130        }?;
131
132        for abs in abs {
133            self.abs_setup(abs.into())?;
134        }
135
136        self.dev_create()
137    }
138
139    /// Write input events to the device
140    pub fn write(&self, events: &[sys::input_event]) -> io::Result<usize> {
141        let events = unsafe { from_raw_parts(events.as_ptr() as *const u8, size_of::<sys::input_event>() * events.len()) };
142        nix::unistd::write(self, events)
143            .map(|c| c / size_of::<sys::input_event>()).map_err(convert_error)
144    }
145
146    /// Read events from uinput (see `EV_UINPUT`)
147    pub fn read(&self, events: &mut [sys::input_event]) -> io::Result<usize> {
148        let events = unsafe { from_raw_parts_mut(events.as_mut_ptr() as *mut u8, size_of::<sys::input_event>() * events.len()) };
149        nix::unistd::read(self.fd(), events)
150            .map(|len| len / size_of::<sys::input_event>()).map_err(convert_error)
151    }
152
153    /// Returns the sysfs directory for the input device.
154    ///
155    /// Note that this path may not exist if sysfs is not mounted in the standard `/sys` location.
156    pub fn sys_path(&self) -> io::Result<PathBuf> {
157        let sys = self.sys_name()?;
158        let sys = CStr::from_bytes_with_nul(&sys).map(|c| c.to_bytes()).unwrap_or(&sys);
159        Ok(Path::new("/sys/devices/virtual/input/").join(OsStr::from_bytes(sys)))
160    }
161
162    /// The device name of the input device.
163    pub fn evdev_name(&self) -> io::Result<OsString> {
164        let sys = self.sys_path()?;
165        fs::read_dir(&sys)?.filter_map(|e| match e {
166            Err(err) => Some(Err(err)),
167            Ok(e) => match e.file_type() {
168                Err(err) => Some(Err(err)),
169                Ok(ty) if ty.is_dir() => {
170                    let name = e.file_name();
171                    if name.as_bytes().starts_with(b"event") {
172                        Some(Ok(e.file_name()))
173                    } else {
174                        None
175                    }
176                },
177                Ok(..) => None,
178            },
179        }).next().unwrap_or_else(|| Err(io::Error::new(io::ErrorKind::NotFound, "event input device not found")))
180    }
181
182    /// The device node path of the input device.
183    ///
184    /// Note that this path may not exist if `/dev/input/*` isn't mounted properly.
185    pub fn evdev_path(&self) -> io::Result<PathBuf> {
186        self.evdev_name().map(|ev| Path::new("/dev/input/").join(ev))
187    }
188
189    ioctl_impl! {
190        {
191            /// `UI_DEV_CREATE`
192            @call dev_create = ui_dev_create
193        }
194        {
195            /// `UI_DEV_DESTROY`
196            @call dev_destroy = ui_dev_destroy
197        }
198        {
199            /// `UI_DEV_SETUP`
200            @set dev_setup(&sys::uinput_setup) = ui_dev_setup
201        }
202        {
203            /// `UI_ABS_SETUP`
204            @set abs_setup(&sys::uinput_abs_setup) = ui_abs_setup
205        }
206        {
207            /// `UI_SET_EVBIT`
208            @set set_evbit(kinds::EventKind) = ui_set_evbit
209        }
210        {
211            /// `UI_SET_KEYBIT`
212            @set set_keybit(Key) = ui_set_keybit
213        }
214        {
215            /// `UI_SET_RELBIT`
216            @set set_relbit(kinds::RelativeAxis) = ui_set_relbit
217        }
218        {
219            /// `UI_SET_ABSBIT`
220            @set set_absbit(kinds::AbsoluteAxis) = ui_set_absbit
221        }
222        {
223            /// `UI_SET_MSCBIT`
224            @set set_mscbit(kinds::MiscKind) = ui_set_mscbit
225        }
226        {
227            /// `UI_SET_LEDBIT`
228            @set set_ledbit(kinds::LedKind) = ui_set_ledbit
229        }
230        {
231            /// `UI_SET_SNDBIT`
232            @set set_sndbit(kinds::SoundKind) = ui_set_sndbit
233        }
234        {
235            /// `UI_SET_FFBIT`
236            @set set_ffbit(kinds::ForceFeedbackKind) = ui_set_ffbit
237        }
238        {
239            /// `UI_SET_PHYS`
240            @set_str set_phys = ui_set_phys
241        }
242        {
243            /// `UI_SET_SWBIT`
244            @set set_swbit(kinds::SwitchKind) = ui_set_swbit
245        }
246        {
247            /// `UI_SET_PROPBIT`
248            @set set_propbit(kinds::InputProperty) = ui_set_propbit
249        }
250        {
251            /// `UI_BEGIN_FF_UPLOAD`
252            @set ff_upload_begin(&mut sys::uinput_ff_upload) = ui_begin_ff_upload
253        }
254        {
255            /// `UI_END_FF_UPLOAD`
256            @set ff_upload_end(&sys::uinput_ff_upload) = ui_end_ff_upload
257        }
258        {
259            /// `UI_BEGIN_FF_ERASE`
260            @set ff_erase_begin(&mut sys::uinput_ff_erase) = ui_begin_ff_erase
261        }
262        {
263            /// `UI_END_FF_ERASE`
264            @set ff_erase_end(&sys::uinput_ff_erase) = ui_end_ff_erase
265        }
266        {
267            /// `UI_GET_SYSNAME`
268            @get_str sys_name, sys_name_buf = ui_get_sysname
269        }
270        {
271            /// `UI_GET_VERSION`
272            @get version = ui_get_version -> u32
273        }
274    }
275}