evdev/
uinput.rs

1//! Virtual device emulation for evdev via uinput.
2//!
3//! This is quite useful when testing/debugging devices, or synchronization.
4
5use crate::compat::{input_event, input_id, uinput_abs_setup, uinput_setup, UINPUT_MAX_NAME_SIZE};
6use crate::ff::FFEffectData;
7use crate::inputid::{BusType, InputId};
8use crate::{
9    sys, AttributeSetRef, FFEffectCode, InputEvent, KeyCode, MiscCode, PropType, RelativeAxisCode,
10    SwitchCode, SynchronizationEvent, UInputCode, UInputEvent, UinputAbsSetup,
11};
12use std::ffi::{CStr, OsStr};
13use std::os::fd::{AsFd, AsRawFd, BorrowedFd, OwnedFd, RawFd};
14use std::os::unix::ffi::OsStrExt;
15use std::path::{Path, PathBuf};
16use std::{fs, io};
17
18const UINPUT_PATH: &str = "/dev/uinput";
19const SYSFS_PATH: &str = "/sys/devices/virtual/input";
20const DEV_PATH: &str = "/dev/input";
21
22/// A builder struct for creating a new uinput virtual device.
23#[derive(Debug)]
24pub struct VirtualDeviceBuilder<'a> {
25    fd: OwnedFd,
26    name: &'a [u8],
27    id: Option<input_id>,
28    ff_effects_max: u32,
29}
30
31/// A builder struct for [`VirtualDevice`].
32///
33/// Created via [`VirtualDevice::builder()`].
34impl<'a> VirtualDeviceBuilder<'a> {
35    #[deprecated(note = "use `VirtualDevice::builder()` instead")]
36    #[doc(hidden)]
37    pub fn new() -> io::Result<Self> {
38        // Open in read-write mode.
39        let fd = fs::OpenOptions::new()
40            .read(true)
41            .write(true)
42            .open(UINPUT_PATH)?;
43
44        Ok(VirtualDeviceBuilder {
45            fd: fd.into(),
46            name: Default::default(),
47            id: None,
48            ff_effects_max: 0,
49        })
50    }
51
52    /// Set the display name of this device.
53    #[inline]
54    pub fn name<S: AsRef<[u8]> + ?Sized>(mut self, name: &'a S) -> Self {
55        self.name = name.as_ref();
56        self
57    }
58
59    /// Set a custom input ID.
60    #[inline]
61    pub fn input_id(mut self, id: InputId) -> Self {
62        self.id = Some(id.0);
63        self
64    }
65
66    /// Set the device's physical location, e.g. `usb-00:01.2-2.1/input0`.
67    pub fn with_phys(self, path: &CStr) -> io::Result<Self> {
68        unsafe {
69            sys::ui_set_phys(self.fd.as_raw_fd(), path.as_ptr())?;
70        }
71        Ok(self)
72    }
73
74    /// Set the key codes that can be emitted by this device.
75    pub fn with_keys(self, keys: &AttributeSetRef<KeyCode>) -> io::Result<Self> {
76        // Run ioctls for setting capability bits
77        unsafe {
78            sys::ui_set_evbit(
79                self.fd.as_raw_fd(),
80                crate::EventType::KEY.0 as nix::sys::ioctl::ioctl_param_type,
81            )?;
82        }
83
84        for bit in keys.iter() {
85            unsafe {
86                sys::ui_set_keybit(
87                    self.fd.as_raw_fd(),
88                    bit.0 as nix::sys::ioctl::ioctl_param_type,
89                )?;
90            }
91        }
92
93        Ok(self)
94    }
95
96    /// Set the absolute axes of this device.
97    pub fn with_absolute_axis(self, axis: &UinputAbsSetup) -> io::Result<Self> {
98        unsafe {
99            sys::ui_set_evbit(
100                self.fd.as_raw_fd(),
101                crate::EventType::ABSOLUTE.0 as nix::sys::ioctl::ioctl_param_type,
102            )?;
103            sys::ui_set_absbit(
104                self.fd.as_raw_fd(),
105                axis.code() as nix::sys::ioctl::ioctl_param_type,
106            )?;
107            sys::ui_abs_setup(self.fd.as_raw_fd(), &axis.0 as *const uinput_abs_setup)?;
108        }
109
110        Ok(self)
111    }
112
113    /// Set the relative axes of this device.
114    pub fn with_relative_axes(self, axes: &AttributeSetRef<RelativeAxisCode>) -> io::Result<Self> {
115        unsafe {
116            sys::ui_set_evbit(
117                self.fd.as_raw_fd(),
118                crate::EventType::RELATIVE.0 as nix::sys::ioctl::ioctl_param_type,
119            )?;
120        }
121
122        for bit in axes.iter() {
123            unsafe {
124                sys::ui_set_relbit(
125                    self.fd.as_raw_fd(),
126                    bit.0 as nix::sys::ioctl::ioctl_param_type,
127                )?;
128            }
129        }
130
131        Ok(self)
132    }
133
134    /// Set the properties of this device.
135    pub fn with_properties(self, switches: &AttributeSetRef<PropType>) -> io::Result<Self> {
136        for bit in switches.iter() {
137            unsafe {
138                sys::ui_set_propbit(
139                    self.fd.as_raw_fd(),
140                    bit.0 as nix::sys::ioctl::ioctl_param_type,
141                )?;
142            }
143        }
144
145        Ok(self)
146    }
147
148    /// Set the switch codes that can be emitted by this device.
149    pub fn with_switches(self, switches: &AttributeSetRef<SwitchCode>) -> io::Result<Self> {
150        unsafe {
151            sys::ui_set_evbit(
152                self.fd.as_raw_fd(),
153                crate::EventType::SWITCH.0 as nix::sys::ioctl::ioctl_param_type,
154            )?;
155        }
156
157        for bit in switches.iter() {
158            unsafe {
159                sys::ui_set_swbit(
160                    self.fd.as_raw_fd(),
161                    bit.0 as nix::sys::ioctl::ioctl_param_type,
162                )?;
163            }
164        }
165
166        Ok(self)
167    }
168
169    /// Set the force-feedback effects that can be emitted by this device.
170    pub fn with_ff(self, ff: &AttributeSetRef<FFEffectCode>) -> io::Result<Self> {
171        unsafe {
172            sys::ui_set_evbit(
173                self.fd.as_raw_fd(),
174                crate::EventType::FORCEFEEDBACK.0 as nix::sys::ioctl::ioctl_param_type,
175            )?;
176        }
177
178        for bit in ff.iter() {
179            unsafe {
180                sys::ui_set_ffbit(
181                    self.fd.as_raw_fd(),
182                    bit.0 as nix::sys::ioctl::ioctl_param_type,
183                )?;
184            }
185        }
186
187        Ok(self)
188    }
189
190    /// Set the maximum number for a force-feedback effect for this device.
191    pub fn with_ff_effects_max(mut self, ff_effects_max: u32) -> Self {
192        self.ff_effects_max = ff_effects_max;
193        self
194    }
195
196    /// Set the `MiscCode`s of this device.
197    pub fn with_msc(self, misc_set: &AttributeSetRef<MiscCode>) -> io::Result<Self> {
198        unsafe {
199            sys::ui_set_evbit(
200                self.fd.as_raw_fd(),
201                crate::EventType::MISC.0 as nix::sys::ioctl::ioctl_param_type,
202            )?;
203        }
204
205        for bit in misc_set.iter() {
206            unsafe {
207                sys::ui_set_mscbit(
208                    self.fd.as_raw_fd(),
209                    bit.0 as nix::sys::ioctl::ioctl_param_type,
210                )?;
211            }
212        }
213
214        Ok(self)
215    }
216
217    /// Finalize and register this device.
218    ///
219    /// # Errors
220    /// Returns an error if device setup or creation fails.
221    pub fn build(self) -> io::Result<VirtualDevice> {
222        // Populate the uinput_setup struct
223
224        let mut usetup = uinput_setup {
225            id: self.id.unwrap_or(DEFAULT_ID),
226            name: [0; UINPUT_MAX_NAME_SIZE],
227            ff_effects_max: self.ff_effects_max,
228        };
229
230        // SAFETY: either casting [u8] to [u8], or [u8] to [i8], which is the same size
231        let name_bytes = unsafe { &*(self.name as *const [u8] as *const [libc::c_char]) };
232        // Panic if we're doing something really stupid
233        // + 1 for the null terminator; usetup.name was zero-initialized so there will be null
234        // bytes after the part we copy into
235        assert!(name_bytes.len() + 1 < UINPUT_MAX_NAME_SIZE);
236        usetup.name[..name_bytes.len()].copy_from_slice(name_bytes);
237
238        VirtualDevice::new(self.fd, &usetup)
239    }
240}
241
242const DEFAULT_ID: input_id = input_id {
243    bustype: BusType::BUS_USB.0,
244    vendor: 0x1234,  /* sample vendor */
245    product: 0x5678, /* sample product */
246    version: 0x111,
247};
248
249/// A handle to a uinput virtual device.
250#[derive(Debug)]
251pub struct VirtualDevice {
252    fd: OwnedFd,
253    pub(crate) event_buf: Vec<input_event>,
254}
255
256impl VirtualDevice {
257    /// Convenience method for creating a `VirtualDeviceBuilder`.
258    pub fn builder<'a>() -> io::Result<VirtualDeviceBuilder<'a>> {
259        #[allow(deprecated)]
260        VirtualDeviceBuilder::new()
261    }
262
263    /// Create a new virtual device.
264    fn new(fd: OwnedFd, usetup: &uinput_setup) -> io::Result<Self> {
265        unsafe { sys::ui_dev_setup(fd.as_raw_fd(), usetup)? };
266        unsafe { sys::ui_dev_create(fd.as_raw_fd())? };
267
268        Ok(VirtualDevice {
269            fd,
270            event_buf: vec![],
271        })
272    }
273
274    #[inline]
275    fn write_raw(&mut self, events: &[InputEvent]) -> io::Result<()> {
276        crate::write_events(self.fd.as_fd(), events)?;
277        Ok(())
278    }
279
280    /// Get the syspath representing this uinput device.
281    ///
282    /// The syspath returned is the one of the input node itself (e.g.
283    /// `/sys/devices/virtual/input/input123`), not the syspath of the device node.
284    pub fn get_syspath(&mut self) -> io::Result<PathBuf> {
285        let mut syspath = vec![0u8; 256];
286        let len = unsafe { sys::ui_get_sysname(self.fd.as_raw_fd(), &mut syspath)? };
287        syspath.truncate(len as usize - 1);
288
289        let syspath = OsStr::from_bytes(&syspath);
290
291        Ok(Path::new(SYSFS_PATH).join(syspath))
292    }
293
294    /// Get the syspaths of the corresponding device nodes in /dev/input.
295    pub fn enumerate_dev_nodes_blocking(&mut self) -> io::Result<DevNodesBlocking> {
296        let path = self.get_syspath()?;
297        let dir = std::fs::read_dir(path)?;
298
299        Ok(DevNodesBlocking { dir })
300    }
301
302    /// Get the syspaths of the corresponding device nodes in /dev/input.
303    #[cfg(feature = "tokio")]
304    pub async fn enumerate_dev_nodes(&mut self) -> io::Result<DevNodes> {
305        let path = self.get_syspath()?;
306        let dir = tokio::fs::read_dir(path).await?;
307
308        Ok(DevNodes { dir })
309    }
310
311    /// Post a batch of events to the virtual device.
312    ///
313    /// The batch is automatically terminated with a `SYN_REPORT` event.
314    /// Events from physical devices are batched based on if they occur simultaneously, for example movement
315    /// of a mouse triggers a movement events for the X and Y axes separately in a batch of 2 events.
316    ///
317    /// Single events such as a `KEY` event must still be followed by a `SYN_REPORT`.
318    pub fn emit(&mut self, events: &[InputEvent]) -> io::Result<()> {
319        self.write_raw(events)?;
320        let syn = *SynchronizationEvent::new(crate::SynchronizationCode::SYN_REPORT, 0);
321        self.write_raw(&[syn])
322    }
323
324    /// Processes the given [`UInputEvent`] if it is a force feedback upload event, in which case
325    /// this function will start the force feedback upload and claim ownership over the
326    /// [`UInputEvent`] and return a [`FFUploadEvent`] instead.
327    ///
328    /// The returned event allows the user to allocate and set the effect ID as well as access the
329    /// effect data.
330    ///
331    /// # Panics
332    ///
333    /// This function will panic if `event.code()` is not `UI_FF_UPLOAD`.
334    pub fn process_ff_upload(&mut self, event: UInputEvent) -> io::Result<FFUploadEvent> {
335        assert_eq!(event.code(), UInputCode::UI_FF_UPLOAD);
336
337        let mut request: sys::uinput_ff_upload = unsafe { std::mem::zeroed() };
338        request.request_id = event.value() as u32;
339        unsafe { sys::ui_begin_ff_upload(self.fd.as_raw_fd(), &mut request)? };
340
341        request.retval = 0;
342
343        let fd = self.fd.try_clone()?;
344
345        Ok(FFUploadEvent { fd, request })
346    }
347
348    /// Processes the given [`UInputEvent`] if it is a force feedback erase event, in which case
349    /// this function will start the force feedback erasure and claim ownership over the
350    /// [`UInputEvent`] and return a [`FFEraseEvent`] instead.
351    ///
352    /// The returned event allows the user to access the effect ID, such that it can free any
353    /// memory used for the given effect ID.
354    ///
355    /// # Panics
356    ///
357    /// This function will panic if `event.code()` is not `UI_FF_ERASE`.
358    pub fn process_ff_erase(&mut self, event: UInputEvent) -> io::Result<FFEraseEvent> {
359        assert_eq!(event.code(), UInputCode::UI_FF_ERASE);
360
361        let mut request: sys::uinput_ff_erase = unsafe { std::mem::zeroed() };
362        request.request_id = event.value() as u32;
363        unsafe { sys::ui_begin_ff_erase(self.fd.as_raw_fd(), &mut request)? };
364
365        request.retval = 0;
366
367        let fd = self.fd.try_clone()?;
368
369        Ok(FFEraseEvent { fd, request })
370    }
371
372    /// Read a maximum of `num` events into the internal buffer. If the underlying fd is not
373    /// O_NONBLOCK, this will block.
374    ///
375    /// Returns the number of events that were read, or an error.
376    pub(crate) fn fill_events(&mut self) -> io::Result<usize> {
377        let fd = self.fd.as_raw_fd();
378        self.event_buf.reserve(crate::EVENT_BATCH_SIZE);
379
380        let spare_capacity = self.event_buf.spare_capacity_mut();
381        let spare_capacity_size = std::mem::size_of_val(spare_capacity);
382
383        // use libc::read instead of nix::unistd::read b/c we need to pass an uninitialized buf
384        let res = unsafe { libc::read(fd, spare_capacity.as_mut_ptr() as _, spare_capacity_size) };
385        let bytes_read = nix::errno::Errno::result(res)?;
386        let num_read = bytes_read as usize / std::mem::size_of::<input_event>();
387        unsafe {
388            let len = self.event_buf.len();
389            self.event_buf.set_len(len + num_read);
390        }
391        Ok(num_read)
392    }
393
394    /// Fetches and returns events from the kernel ring buffer without doing synchronization on
395    /// SYN_DROPPED.
396    ///
397    /// By default this will block until events are available. Typically, users will want to call
398    /// this in a tight loop within a thread.
399    pub fn fetch_events(&mut self) -> io::Result<impl Iterator<Item = InputEvent> + '_> {
400        self.fill_events()?;
401        Ok(self.event_buf.drain(..).map(InputEvent::from))
402    }
403
404    #[cfg(feature = "tokio")]
405    #[inline]
406    pub fn into_event_stream(self) -> io::Result<VirtualEventStream> {
407        VirtualEventStream::new(self)
408    }
409}
410
411/// This struct is returned from the [VirtualDevice::enumerate_dev_nodes_blocking] function and will yield
412/// the syspaths corresponding to the virtual device. These are of the form `/dev/input123`.
413pub struct DevNodesBlocking {
414    dir: std::fs::ReadDir,
415}
416
417impl Iterator for DevNodesBlocking {
418    type Item = io::Result<PathBuf>;
419
420    fn next(&mut self) -> Option<Self::Item> {
421        for entry in self.dir.by_ref() {
422            let entry = match entry {
423                Ok(entry) => entry,
424                Err(e) => return Some(Err(e)),
425            };
426
427            // Map the directory name to its file name.
428            let file_name = entry.file_name();
429
430            // Ignore file names that do not start with event.
431            if !file_name.as_bytes().starts_with(b"event") {
432                continue;
433            }
434
435            // Construct the path of the form '/dev/input/eventX'.
436            let path = Path::new(DEV_PATH).join(file_name);
437
438            return Some(Ok(path));
439        }
440
441        None
442    }
443}
444
445/// This struct is returned from the [VirtualDevice::enumerate_dev_nodes_blocking] function and
446/// will yield the syspaths corresponding to the virtual device. These are of the form
447/// `/dev/input123`.
448#[cfg(feature = "tokio")]
449pub struct DevNodes {
450    dir: tokio::fs::ReadDir,
451}
452
453#[cfg(feature = "tokio")]
454impl DevNodes {
455    /// Returns the next entry in the set of device nodes.
456    pub async fn next_entry(&mut self) -> io::Result<Option<PathBuf>> {
457        while let Some(entry) = self.dir.next_entry().await? {
458            // Map the directory name to its file name.
459            let file_name = entry.file_name();
460
461            // Ignore file names that do not start with event.
462            if !file_name.as_bytes().starts_with(b"event") {
463                continue;
464            }
465
466            // Construct the path of the form '/dev/input/eventX'.
467            let path = Path::new(DEV_PATH).join(file_name);
468
469            return Ok(Some(path));
470        }
471
472        Ok(None)
473    }
474}
475
476impl AsFd for VirtualDevice {
477    fn as_fd(&self) -> BorrowedFd<'_> {
478        self.fd.as_fd()
479    }
480}
481
482impl AsRawFd for VirtualDevice {
483    fn as_raw_fd(&self) -> RawFd {
484        self.fd.as_raw_fd()
485    }
486}
487
488/// Represents a force feedback upload event that we are currently processing.
489pub struct FFUploadEvent {
490    fd: OwnedFd,
491    request: sys::uinput_ff_upload,
492}
493
494impl FFUploadEvent {
495    /// Returns the old effect data.
496    pub fn old_effect(&self) -> FFEffectData {
497        self.request.old.into()
498    }
499
500    /// Returns the new effect ID.
501    pub fn effect_id(&self) -> i16 {
502        self.request.effect.id
503    }
504
505    /// Sets the new effect ID.
506    pub fn set_effect_id(&mut self, id: i16) {
507        self.request.effect.id = id;
508    }
509
510    /// Returns the new effect data.
511    pub fn effect(&self) -> FFEffectData {
512        self.request.effect.into()
513    }
514
515    /// Returns the currently set return value for the upload event.
516    pub fn retval(&self) -> i32 {
517        self.request.retval
518    }
519
520    /// Sets the return value to return for the upload event.
521    pub fn set_retval(&mut self, value: i32) {
522        self.request.retval = value;
523    }
524}
525
526impl Drop for FFUploadEvent {
527    fn drop(&mut self) {
528        unsafe {
529            let _ = sys::ui_end_ff_upload(self.fd.as_raw_fd(), &self.request);
530        }
531    }
532}
533
534/// Represents a force feedback erase event that we are currently processing.
535pub struct FFEraseEvent {
536    fd: OwnedFd,
537    request: sys::uinput_ff_erase,
538}
539
540impl FFEraseEvent {
541    /// Returns the effect ID to erase.
542    pub fn effect_id(&self) -> u32 {
543        self.request.effect_id
544    }
545
546    /// Returns the currently set return value for the erase event.
547    pub fn retval(&self) -> i32 {
548        self.request.retval
549    }
550
551    /// Sets the return value to return for the erase event.
552    pub fn set_retval(&mut self, value: i32) {
553        self.request.retval = value;
554    }
555}
556
557impl Drop for FFEraseEvent {
558    fn drop(&mut self) {
559        unsafe {
560            let _ = sys::ui_end_ff_erase(self.fd.as_raw_fd(), &self.request);
561        }
562    }
563}
564
565#[cfg(feature = "tokio")]
566mod tokio_stream {
567    use super::*;
568
569    use std::future::poll_fn;
570    use std::task::{ready, Context, Poll};
571    use tokio::io::unix::AsyncFd;
572
573    /// An asynchronous stream of input events.
574    ///
575    /// This can be used by calling [`stream.next_event().await?`](Self::next_event), or if you
576    /// need to pass it as a stream somewhere, the [`futures::Stream`](Stream) implementation.
577    /// There's also a lower-level [`Self::poll_event`] function if you need to fetch an event from
578    /// inside a `Future::poll` impl.
579    pub struct VirtualEventStream {
580        device: AsyncFd<VirtualDevice>,
581        index: usize,
582    }
583    impl Unpin for VirtualEventStream {}
584
585    impl VirtualEventStream {
586        pub(crate) fn new(device: VirtualDevice) -> io::Result<Self> {
587            use nix::fcntl;
588            fcntl::fcntl(device.as_raw_fd(), fcntl::F_SETFL(fcntl::OFlag::O_NONBLOCK))?;
589            let device = AsyncFd::new(device)?;
590            Ok(Self { device, index: 0 })
591        }
592
593        /// Returns a reference to the underlying device
594        pub fn device(&self) -> &VirtualDevice {
595            self.device.get_ref()
596        }
597
598        /// Returns a mutable reference to the underlying device.
599        pub fn device_mut(&mut self) -> &mut VirtualDevice {
600            self.device.get_mut()
601        }
602
603        /// Try to wait for the next event in this stream. Any errors are likely to be fatal, i.e.
604        /// any calls afterwards will likely error as well.
605        pub async fn next_event(&mut self) -> io::Result<InputEvent> {
606            poll_fn(|cx| self.poll_event(cx)).await
607        }
608
609        /// A lower-level function for directly polling this stream.
610        pub fn poll_event(&mut self, cx: &mut Context<'_>) -> Poll<io::Result<InputEvent>> {
611            'outer: loop {
612                if let Some(&ev) = self.device.get_ref().event_buf.get(self.index) {
613                    self.index += 1;
614                    return Poll::Ready(Ok(InputEvent::from(ev)));
615                }
616
617                self.device.get_mut().event_buf.clear();
618                self.index = 0;
619
620                loop {
621                    let mut guard = ready!(self.device.poll_read_ready_mut(cx))?;
622
623                    let res = guard.try_io(|device| device.get_mut().fill_events());
624                    match res {
625                        Ok(res) => {
626                            let _ = res?;
627                            continue 'outer;
628                        }
629                        Err(_would_block) => continue,
630                    }
631                }
632            }
633        }
634    }
635
636    #[cfg(feature = "stream-trait")]
637    impl futures_core::Stream for VirtualEventStream {
638        type Item = io::Result<InputEvent>;
639        fn poll_next(
640            self: std::pin::Pin<&mut Self>,
641            cx: &mut Context<'_>,
642        ) -> Poll<Option<Self::Item>> {
643            self.get_mut().poll_event(cx).map(Some)
644        }
645    }
646}
647#[cfg(feature = "tokio")]
648pub use tokio_stream::VirtualEventStream;