evdevil/
uinput.rs

1//! Userspace input devices.
2//!
3//! This module allows writing device drivers and virtual input devices in Rust.
4//!
5//! A [`UinputDevice`] can be created via [`UinputDevice::builder`] and will create a corresponding
6//! evdev input device that other applications (or *this* application) can read events from.
7
8use std::{
9    error::Error,
10    ffi::{CStr, CString, OsString, c_char, c_int},
11    fmt,
12    fs::File,
13    io::{self, Read as _},
14    mem::{self, MaybeUninit},
15    os::{
16        fd::{AsFd, AsRawFd, BorrowedFd, FromRawFd, IntoRawFd},
17        unix::{ffi::OsStringExt, prelude::RawFd},
18    },
19    ptr, slice,
20    time::Instant,
21};
22
23use linux_ioctl::Ioctl;
24
25use crate::{
26    AbsInfo, InputId, InputProp, KeyRepeat, Slot,
27    batch::BatchWriter,
28    drop::on_drop,
29    event::{
30        Abs, AbsEvent, EventType, InputEvent, Key, Led, Misc, Rel, Repeat, RepeatEvent, Sound,
31        Switch, Syn, SynEvent, UinputCode, UinputEvent,
32    },
33    ff::{self, Effect, EffectId},
34    raw::{
35        input::ff_effect,
36        uinput::{
37            UI_ABS_SETUP, UI_BEGIN_FF_ERASE, UI_BEGIN_FF_UPLOAD, UI_DEV_CREATE, UI_DEV_SETUP,
38            UI_END_FF_ERASE, UI_END_FF_UPLOAD, UI_GET_SYSNAME, UI_GET_VERSION, UI_SET_ABSBIT,
39            UI_SET_EVBIT, UI_SET_FFBIT, UI_SET_KEYBIT, UI_SET_LEDBIT, UI_SET_MSCBIT, UI_SET_PHYS,
40            UI_SET_PROPBIT, UI_SET_RELBIT, UI_SET_SNDBIT, UI_SET_SWBIT, UINPUT_MAX_NAME_SIZE,
41            uinput_abs_setup, uinput_ff_erase, uinput_ff_upload, uinput_setup,
42        },
43    },
44    util::{block_until_readable, errorkind2libc, is_readable, set_nonblocking},
45};
46
47/// Absolute axis setup information.
48///
49/// Used by [`Builder::with_abs_axes`].
50#[derive(Clone, Copy, PartialEq, Eq)]
51#[repr(transparent)]
52pub struct AbsSetup(uinput_abs_setup);
53
54impl fmt::Debug for AbsSetup {
55    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
56        f.debug_struct("AbsSetup")
57            .field("abs", &self.abs())
58            .field("abs_info", self.abs_info())
59            .finish()
60    }
61}
62
63impl AbsSetup {
64    pub const fn new(abs: Abs, abs_info: AbsInfo) -> Self {
65        AbsSetup(uinput_abs_setup {
66            code: abs.raw(),
67            absinfo: abs_info.0,
68        })
69    }
70
71    pub const fn abs(&self) -> Abs {
72        Abs::from_raw(self.0.code)
73    }
74
75    pub const fn abs_info(&self) -> &AbsInfo {
76        // Safety: `AbsInfo` is a `#[repr(transparent)]` wrapper
77        unsafe { mem::transmute(&self.0.absinfo) }
78    }
79}
80
81/// A builder for creating a [`UinputDevice`].
82///
83/// Returned by [`UinputDevice::builder`].
84pub struct Builder {
85    file: File, // handle to `/dev/uinput`
86    setup: uinput_setup,
87}
88
89impl fmt::Debug for Builder {
90    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
91        f.debug_struct("Builder")
92            .field("file", &self.file)
93            .field("input_id", &InputId(self.setup.id))
94            .field("ff_effects_max", &self.setup.ff_effects_max)
95            .finish()
96    }
97}
98
99impl Builder {
100    fn new() -> io::Result<Self> {
101        let file = File::options().read(true).write(true).open("/dev/uinput")?;
102        unsafe {
103            let mut version = 0;
104            UI_GET_VERSION.ioctl(&file, &mut version)?;
105            log::debug!("opened /dev/uinput; version={version:#x}");
106        }
107        Ok(Self {
108            file,
109            setup: unsafe { mem::zeroed() },
110        })
111    }
112
113    /// Configures the device's hardware IDs.
114    pub fn with_device_id(mut self, id: InputId) -> io::Result<Self> {
115        self.setup.id = id.0;
116        Ok(self)
117    }
118
119    /// Sets the physical path of the device.
120    ///
121    /// By default, the physical path of a `uinput` device is unset, and the corresponding
122    /// [`Evdev::phys`] method will return [`None`].
123    ///
124    /// This method can be used to change that behavior and expose the proper hardware location to
125    /// consumers.
126    ///
127    /// [`Evdev::phys`]: crate::Evdev::phys
128    pub fn with_phys(self, path: &str) -> io::Result<Self> {
129        self.with_phys_cstr(&CString::new(path).unwrap())
130    }
131
132    /// Sets the physical path of the device to a [`CStr`].
133    ///
134    /// It is typically easier to use [`Builder::with_phys`] instead.
135    pub fn with_phys_cstr(self, path: &CStr) -> io::Result<Self> {
136        unsafe {
137            UI_SET_PHYS.ioctl(&self.file, path.as_ptr().cast())?;
138        }
139
140        Ok(self)
141    }
142
143    /// Sets the given [`InputProp`]s for the device.
144    ///
145    /// [`InputProp`]s can be used to advertise a specific type of device, like a drawing tablet.
146    pub fn with_props(self, props: impl IntoIterator<Item = InputProp>) -> io::Result<Self> {
147        for prop in props {
148            unsafe {
149                UI_SET_PROPBIT.ioctl(&self.file, prop.0.into())?;
150            }
151        }
152        Ok(self)
153    }
154
155    /// Enables the given list of [`Key`]s to be reported by the device.
156    pub fn with_keys(self, keys: impl IntoIterator<Item = Key>) -> io::Result<Self> {
157        self.enable_codes(
158            UI_SET_KEYBIT,
159            EventType::KEY,
160            keys.into_iter().map(|v| v.raw().into()),
161        )?;
162        Ok(self)
163    }
164
165    /// Enables the given list of [`Rel`]ative axes to be reported by the device.
166    pub fn with_rel_axes(self, rel: impl IntoIterator<Item = Rel>) -> io::Result<Self> {
167        self.enable_codes(
168            UI_SET_RELBIT,
169            EventType::REL,
170            rel.into_iter().map(|v| v.raw().into()),
171        )?;
172        Ok(self)
173    }
174
175    /// Enables the given list of [`Misc`] events to be reported by the device.
176    pub fn with_misc(self, misc: impl IntoIterator<Item = Misc>) -> io::Result<Self> {
177        self.enable_codes(
178            UI_SET_MSCBIT,
179            EventType::MSC,
180            misc.into_iter().map(|v| v.raw().into()),
181        )?;
182        Ok(self)
183    }
184
185    /// Enables the given list of [`Led`]s.
186    ///
187    /// LEDs may be controlled by either the `uinput` or `evdev` side, by writing the appropriate
188    /// event to the stream.
189    pub fn with_leds(self, leds: impl IntoIterator<Item = Led>) -> io::Result<Self> {
190        self.enable_codes(
191            UI_SET_LEDBIT,
192            EventType::LED,
193            leds.into_iter().map(|v| v.raw().into()),
194        )?;
195        Ok(self)
196    }
197
198    /// Enables the given list of [`Sound`]s.
199    ///
200    /// Sounds are typically played by an [`Evdev`][crate::Evdev] handle by writing the appropriate
201    /// event to the stream.
202    pub fn with_sounds(self, sounds: impl IntoIterator<Item = Sound>) -> io::Result<Self> {
203        self.enable_codes(
204            UI_SET_SNDBIT,
205            EventType::SND,
206            sounds.into_iter().map(|v| v.raw().into()),
207        )?;
208        Ok(self)
209    }
210
211    /// Enables the given list of [`Switch`]es to be reported by the device.
212    pub fn with_switches(self, switches: impl IntoIterator<Item = Switch>) -> io::Result<Self> {
213        self.enable_codes(
214            UI_SET_SWBIT,
215            EventType::SW,
216            switches.into_iter().map(|v| v.raw().into()),
217        )?;
218        Ok(self)
219    }
220
221    /// Enables the given list of absolute axes.
222    ///
223    /// The [`AbsInfo`] associated with an axis may be changed by an [`Evdev`][crate::Evdev] client
224    /// via [`Evdev::set_abs_info`][crate::Evdev::set_abs_info].
225    pub fn with_abs_axes(self, axes: impl IntoIterator<Item = AbsSetup>) -> io::Result<Self> {
226        self.enable_event(EventType::ABS)?;
227        for setup in axes {
228            unsafe {
229                UI_SET_ABSBIT.ioctl(&self.file, setup.0.code as c_int)?;
230                UI_ABS_SETUP.ioctl(&self.file, &setup.0)?;
231            }
232        }
233        Ok(self)
234    }
235
236    /// Sets the maximum number of force-feedback effects that can be played at once.
237    ///
238    /// If this is greater than 0, the device will advertise support for [`EventType::FF`] events.
239    ///
240    /// Note that you also have to enable the specific force-feedback features you intend to support
241    /// by calling [`Builder::with_ff_features`].
242    pub fn with_ff_effects_max(mut self, ff_max: u32) -> io::Result<Self> {
243        self.setup.ff_effects_max = ff_max;
244        Ok(self)
245    }
246
247    /// Advertises the given force-feedback capabilities.
248    ///
249    /// If you call this method, you also have to call [`Builder::with_ff_effects_max`] to configure
250    /// the maximum number of force-feedback effects the device can accept, or the functionality
251    /// won't work.
252    pub fn with_ff_features(self, feat: impl IntoIterator<Item = ff::Feature>) -> io::Result<Self> {
253        self.enable_codes(
254            UI_SET_FFBIT,
255            EventType::FF,
256            feat.into_iter().map(|v| v.0.into()),
257        )?;
258        Ok(self)
259    }
260
261    /// Enables support for autorepeat.
262    ///
263    /// If this is called, [`RepeatEvent`]s may be written to the stream to change the autorepeat
264    /// settings.
265    /// This will also allow [`Evdev`][crate::Evdev] clients to query and modify the key repeat
266    /// settings via [`Evdev::key_repeat`][crate::Evdev::key_repeat] and
267    /// [`Evdev::set_key_repeat`][crate::Evdev::set_key_repeat].
268    pub fn with_key_repeat(self) -> io::Result<Self> {
269        // NOTE: cannot take the `KeyRepeat` as an argument because it has to be written to the stream
270        self.enable_event(EventType::REP)?;
271        Ok(self)
272    }
273
274    // Will return `EINVAL` when attempting to enable a code above the maximum for that type of code.
275    fn enable_codes(
276        &self,
277        ioctl: Ioctl<c_int>,
278        event: EventType,
279        codes: impl IntoIterator<Item = usize>,
280    ) -> io::Result<()> {
281        // Note: these will all yield `EINVAL` with out-of-range indices
282        self.enable_event(event)?;
283        for code in codes {
284            unsafe {
285                ioctl.ioctl(&self.file, code as c_int)?;
286            }
287        }
288        Ok(())
289    }
290
291    fn enable_event(&self, event: EventType) -> io::Result<()> {
292        unsafe {
293            UI_SET_EVBIT.ioctl(&self.file, event.0 as c_int)?;
294        }
295        Ok(())
296    }
297
298    /// Creates the `uinput` device.
299    ///
300    /// After this method returns successfully, the device will show up in `/dev/input` and emit
301    /// hotplug events accordingly.
302    ///
303    /// **NOTE**: Because of how `udev` works, devices can show up with incorrect permission bits
304    /// for a short time, before those permissions are set correctly by the system.
305    /// This means that calling [`enumerate`][crate::enumerate] immediately after creating a
306    /// `uinput` device (or immediately after plugging in a physical device) might fail to access
307    /// the device.
308    /// However, *hotplug* events should arrive only after the device has been given the correct
309    /// permissions.
310    ///
311    /// # Parameters
312    ///
313    /// - `name`: The name of the device. Should be ASCII, and must not be longer than 79 bytes, or
314    ///   this method will return an error.
315    pub fn build(mut self, name: &str) -> io::Result<UinputDevice> {
316        if name.len() >= UINPUT_MAX_NAME_SIZE {
317            return Err(io::Error::new(
318                io::ErrorKind::InvalidInput,
319                "uinput device name is too long",
320            ));
321        }
322
323        unsafe {
324            ptr::copy_nonoverlapping(
325                name.as_ptr(),
326                self.setup.name.as_mut_ptr().cast(),
327                name.len(),
328            );
329        }
330
331        unsafe {
332            UI_DEV_SETUP.ioctl(&self.file, &self.setup)?;
333            UI_DEV_CREATE.ioctl(&self.file)?;
334        }
335        Ok(UinputDevice { file: self.file })
336    }
337}
338
339/// A virtual `uinput` device.
340#[derive(Debug)]
341pub struct UinputDevice {
342    // NOTE: we deliberately don't call `UI_DEV_DESTROY` on drop, since there can be multiple
343    // `UinputDevice` handles referring to the same device or file description due to `try_clone`.
344    // Closing the last handle to the device will already make the kernel clean everything up
345    // anyways, to using the ioctl seems unnecessary.
346    file: File,
347}
348
349impl AsFd for UinputDevice {
350    #[inline]
351    fn as_fd(&self) -> BorrowedFd<'_> {
352        // Safety: we own the fd, so this lifetime constrains it properly
353        unsafe { BorrowedFd::borrow_raw(self.as_raw_fd()) }
354    }
355}
356
357impl AsRawFd for UinputDevice {
358    #[inline]
359    fn as_raw_fd(&self) -> RawFd {
360        self.file.as_raw_fd()
361    }
362}
363
364impl IntoRawFd for UinputDevice {
365    #[inline]
366    fn into_raw_fd(self) -> RawFd {
367        self.file.into_raw_fd()
368    }
369}
370
371impl FromRawFd for UinputDevice {
372    #[inline]
373    unsafe fn from_raw_fd(fd: RawFd) -> Self {
374        Self {
375            file: unsafe { File::from_raw_fd(fd) },
376        }
377    }
378}
379
380impl UinputDevice {
381    /// Returns a [`Builder`] for configuring a new input device.
382    pub fn builder() -> io::Result<Builder> {
383        Builder::new()
384    }
385
386    /// Moves this handle into or out of non-blocking mode.
387    ///
388    /// Returns whether the [`UinputDevice`] was previously in non-blocking mode.
389    pub fn set_nonblocking(&self, nonblocking: bool) -> io::Result<bool> {
390        set_nonblocking(self.as_raw_fd(), nonblocking)
391    }
392
393    /// Creates a new [`UinputDevice`] instance that refers to the same underlying file handle.
394    ///
395    /// All properties, such as whether the handle is in non-blocking mode, will be shared between
396    /// the instances.
397    pub fn try_clone(&self) -> io::Result<Self> {
398        Ok(Self {
399            file: self.file.try_clone()?,
400        })
401    }
402
403    /// Executes `ioctl` and adds context to the error.
404    unsafe fn ioctl<T>(&self, name: &'static str, ioctl: Ioctl<T>, arg: T) -> io::Result<c_int> {
405        match unsafe { ioctl.ioctl(self, arg) } {
406            Ok(ok) => Ok(ok),
407            Err(e) => {
408                #[derive(Debug)]
409                struct WrappedError {
410                    cause: io::Error,
411                    msg: String,
412                }
413
414                impl fmt::Display for WrappedError {
415                    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
416                        f.write_str(&self.msg)
417                    }
418                }
419                impl Error for WrappedError {
420                    fn source(&self) -> Option<&(dyn Error + 'static)> {
421                        Some(&self.cause)
422                    }
423                }
424
425                log::trace!("ioctl {name} failed with error {e} ({:?})", e.kind());
426                let msg = format!("ioctl {name} failed ({:?})", e.kind());
427                Err(io::Error::new(e.kind(), WrappedError { cause: e, msg }))
428            }
429        }
430    }
431
432    unsafe fn fetch_string(
433        &self,
434        ioctl_name: &'static str,
435        ioctl: fn(usize) -> Ioctl<*mut c_char>,
436    ) -> io::Result<OsString> {
437        // "fetch string" ioctls will return the number of bytes they've copied into our buffer.
438        // This will be at most the length of the buffer. If that happens, some bytes might be lost,
439        // so we retry the call after doubling the buffer size.
440
441        const INITIAL_LEN: usize = 64;
442        let mut buf = vec![0_u8; INITIAL_LEN];
443        let len = loop {
444            let len = unsafe {
445                self.ioctl(
446                    ioctl_name,
447                    ioctl(buf.len()),
448                    buf.as_mut_ptr() as *mut c_char,
449                )?
450            };
451            if len as usize == buf.len() {
452                // Not enough space; double the buffer size and retry.
453                buf.resize(buf.len() * 2, 0);
454            } else {
455                break len;
456            }
457        };
458
459        // `len` includes the trailing 0 byte
460        buf.truncate(len.saturating_sub(1) as usize);
461
462        Ok(OsString::from_vec(buf))
463    }
464
465    /// Retrieves the uinput device's directory name in the sysfs hierarchy.
466    ///
467    /// The full path to the directory is `/sys/devices/virtual/input/` followed by the name
468    /// returned by this method.
469    ///
470    /// This functionality is generally non-portable and only works on Linux.
471    pub fn sysname(&self) -> io::Result<OsString> {
472        unsafe { self.fetch_string("UI_GET_SYSNAME", UI_GET_SYSNAME) }
473    }
474
475    /// Returns an iterator over events *received* by this [`UinputDevice`].
476    ///
477    /// If the device exposes any of the following functionality, it should read events that trigger
478    /// it from this iterator and act accordingly:
479    ///
480    /// - LEDs (via [`Builder::with_leds`]).
481    /// - Sounds (via [`Builder::with_sounds`]).
482    /// - Force Feedback (via [`Builder::with_ff_features`] and [`Builder::with_ff_effects_max`]).
483    ///
484    /// LEDs and Sounds simply need to be triggered or toggled when encountering a matching
485    /// [`LedEvent`] or [`SoundEvent`].
486    ///
487    /// Force feedback is a bit more involved:
488    /// - An attempt to upload a force-feedback effect is signaled by a [`UinputEvent`] sent to the
489    ///   [`UinputDevice`].
490    /// - The uinput device then has to call [`UinputDevice::ff_upload`] to perform the upload.
491    /// - The kernel driver will assign an [`EffectId`] to the effect (later used to start and stop
492    ///   it) and make the uploaded effect data available to the uinput device.
493    ///
494    /// Effect deletion works the same way:
495    /// - When a client wants to delete an effect, a [`UinputEvent`] will be sent to the device.
496    /// - The device then has to call [`UinputDevice::ff_erase`] to perform the deletion.
497    ///
498    /// In both cases, the evdev client will block until [`UinputDevice::ff_upload`] or
499    /// [`UinputDevice::ff_erase`] has been called.
500    ///
501    /// [`LedEvent`]: crate::event::LedEvent
502    /// [`SoundEvent`]: crate::event::SoundEvent
503    /// [`ForceFeedbackEvent`]: crate::event::ForceFeedbackEvent
504    pub fn events(&self) -> Events<'_> {
505        Events { dev: self }
506    }
507
508    /// Deprecated alias of [`UinputDevice::is_readable`].
509    #[deprecated(note = "renamed to `is_readable`")]
510    pub fn can_read(&self) -> io::Result<bool> {
511        is_readable(self.as_raw_fd())
512    }
513
514    /// Returns whether this device has any pending events that can be read without blocking.
515    ///
516    /// If this returns `true`, calling [`UinputDevice::events()`] and then calling
517    /// [`Events::next()`] is guaranteed to not block (but only for a single event).
518    pub fn is_readable(&self) -> io::Result<bool> {
519        is_readable(self.as_raw_fd())
520    }
521
522    /// Blocks the calling thread until [`UinputDevice::is_readable`] would return `true`.
523    ///
524    /// This will block even if `self` is in non-blocking mode (via
525    /// [`UinputDevice::set_nonblocking`]).
526    /// For checking whether events can be read from `self` without blocking, use
527    /// [`UinputDevice::is_readable`], which will *never* block.
528    ///
529    /// If `self` is already readable, this will return immediately.
530    pub fn block_until_readable(&self) -> io::Result<()> {
531        block_until_readable(self.as_raw_fd())
532    }
533
534    /// Performs a requested force-feedback effect upload.
535    ///
536    /// This should be called when receiving a [`UinputEvent`] with a code of
537    /// [`UinputCode::FF_UPLOAD`].
538    ///
539    /// If `handler` returns an error, that error will both be returned to the caller of `ff_upload`
540    /// and also to whichever process attempted to upload the effect.
541    /// This requires a lossy conversion to a C style `Exyz` error constant.
542    /// If `handler` returns a native OS error (eg. via [`io::Error::last_os_error`]), we'll return
543    /// that error code directly.
544    /// Otherwise, we'll try to translate the [`io::ErrorKind`] of the error to something sensible.
545    pub fn ff_upload<R>(
546        &self,
547        request: &UinputEvent,
548        handler: impl FnOnce(&ForceFeedbackUpload) -> io::Result<R>,
549    ) -> io::Result<R> {
550        assert!(request.code() == UinputCode::FF_UPLOAD);
551
552        let mut upload = unsafe { mem::zeroed::<ForceFeedbackUpload>() };
553        upload.0.request_id = request.raw_value() as u32;
554
555        let now = Instant::now();
556        let _d = on_drop(|| log::trace!("`ff_upload` took {:?}", now.elapsed()));
557        unsafe {
558            self.ioctl("UI_BEGIN_FF_UPLOAD", UI_BEGIN_FF_UPLOAD, &mut upload.0)?;
559        }
560
561        let res = handler(&upload);
562        match &res {
563            Ok(_) => {}
564            Err(e) => {
565                let os_err = e.raw_os_error();
566                let errno = e
567                    .raw_os_error()
568                    .unwrap_or_else(|| errorkind2libc(e.kind()).unwrap_or(libc::EIO));
569                log::debug!(
570                    "ff_upload handler errored: {e} ({:?}, OS error: {os_err:?}) -> code {errno}",
571                    e.kind()
572                );
573                upload.0.retval = -errno;
574            }
575        }
576
577        unsafe {
578            self.ioctl("UI_END_FF_UPLOAD", UI_END_FF_UPLOAD, &upload.0)?;
579        }
580
581        res
582    }
583
584    /// Performs a requested force-feedback effect erasure.
585    ///
586    /// This should be called when receiving a [`UinputEvent`] with a code of
587    /// [`UinputCode::FF_ERASE`].
588    ///
589    /// If `handler` returns an error, that error will both be returned to the caller of `ff_erase`
590    /// and also to whichever process attempted to upload the effect.
591    /// This requires a lossy conversion to a C style `Exyz` error constant.
592    /// If `handler` returns a native OS error (eg. via [`io::Error::last_os_error`]), we'll return
593    /// that error code directly.
594    /// Otherwise, we'll try to translate the [`io::ErrorKind`] of the error to something sensible.
595    pub fn ff_erase(
596        &self,
597        request: &UinputEvent,
598        handler: impl FnOnce(&ForceFeedbackErase) -> io::Result<()>,
599    ) -> io::Result<()> {
600        assert!(request.code() == UinputCode::FF_ERASE);
601
602        let mut erase = unsafe { mem::zeroed::<ForceFeedbackErase>() };
603        erase.0.request_id = request.raw_value() as u32;
604        unsafe {
605            self.ioctl("UI_BEGIN_FF_ERASE", UI_BEGIN_FF_ERASE, &mut erase.0)?;
606        }
607
608        match handler(&erase) {
609            Ok(()) => {}
610            Err(e) => {
611                let os_err = e.raw_os_error();
612                let errno = e
613                    .raw_os_error()
614                    .unwrap_or_else(|| errorkind2libc(e.kind()).unwrap_or(libc::EIO));
615                log::debug!(
616                    "ff_erase handler errored: {e} ({:?}, OS error: {os_err:?}) -> code {errno}",
617                    e.kind()
618                );
619                erase.0.retval = -errno;
620            }
621        }
622
623        unsafe {
624            self.ioctl("UI_END_FF_ERASE", UI_END_FF_ERASE, &erase.0)?;
625        }
626
627        Ok(())
628    }
629
630    /// Writes a batch of input events to the device.
631    ///
632    /// The event batch will be automatically followed up by a `SYN_REPORT` event.
633    ///
634    /// # Kernel Processing
635    ///
636    /// The kernel will discard invalid events, events that don't correspond with an event type
637    /// that was enabled during construction, as well as redundant events (whose value matches the
638    /// current state of the button/axis).
639    ///
640    /// It will also set the event timestamp to the current time (at least it will do this if the
641    /// time stamp is zero).
642    ///
643    /// [`RelEvent`]s will always be forwarded to readers, since there is no state associated with
644    /// them.
645    ///
646    /// [`RelEvent`]: crate::event::RelEvent
647    pub fn write(&self, events: &[InputEvent]) -> io::Result<()> {
648        self.writer().write(events)?.finish()?;
649        Ok(())
650    }
651
652    /// Returns an [`EventWriter`] for writing events to the device.
653    ///
654    /// Call [`EventWriter::finish`] to write a `SYN_REPORT` event and end the event batch.
655    ///
656    /// The same considerations as for [`UinputDevice::write`] apply to using the [`EventWriter`].
657    pub fn writer(&self) -> EventWriter<'_> {
658        EventWriter {
659            file: &self.file,
660            batch: BatchWriter::new(),
661            needs_syn_report: true,
662        }
663    }
664}
665
666/// Helper for writing a sequence of events to the uinput device.
667///
668/// Returned by [`UinputDevice::writer`].
669#[derive(Debug)]
670#[must_use = "must call `EventWriter::finish` to flush the event batch"]
671pub struct EventWriter<'a> {
672    file: &'a File,
673    batch: BatchWriter,
674    needs_syn_report: bool,
675}
676
677impl<'a> EventWriter<'a> {
678    /// Writes raw events to the device.
679    ///
680    /// Events passed to this method may be buffered to improve performance.
681    pub fn write(mut self, events: &[InputEvent]) -> io::Result<Self> {
682        self.batch.write(events, self.file)?;
683        Ok(self)
684    }
685
686    /// Prepares for modification of a multi-touch slot.
687    ///
688    /// This will publish an `ABS_MT_SLOT` event with the selected slot.
689    ///
690    /// Returns an [`SlotWriter`] that can be used to modify `slot`.
691    pub fn slot(mut self, slot: impl TryInto<Slot>) -> io::Result<SlotWriter<'a>> {
692        let slot: Slot = slot
693            .try_into()
694            .map_err(|_| io::Error::new(io::ErrorKind::InvalidInput, "invalid slot"))?;
695        self = self.write(&[AbsEvent::new(Abs::MT_SLOT, slot.raw() as i32).into()])?;
696        Ok(SlotWriter(self))
697    }
698
699    /// Changes the device's [`KeyRepeat`] configuration.
700    ///
701    /// Requires that [`Builder::with_key_repeat`] was called to enable the autorepeat
702    /// functionality.
703    ///
704    /// This will write 2 [`RepeatEvent`]s: one with [`Repeat::PERIOD`] and one with
705    /// [`Repeat::DELAY`]. The uinput system will immediately echo both events back to the
706    /// [`UinputDevice`], and to every connected `evdev` client.
707    pub fn set_key_repeat(self, rep: KeyRepeat) -> io::Result<Self> {
708        self.write(&[
709            RepeatEvent::new(Repeat::PERIOD, rep.period()).into(),
710            RepeatEvent::new(Repeat::DELAY, rep.delay()).into(),
711        ])
712    }
713
714    /// Finishes this batch of events by sending a `SYN_REPORT` event.
715    pub fn finish(mut self) -> io::Result<()> {
716        self.finish_impl()?;
717        Ok(())
718    }
719
720    fn finish_impl(&mut self) -> io::Result<()> {
721        if self.needs_syn_report {
722            self.needs_syn_report = false;
723            self.batch
724                .write(&[SynEvent::new(Syn::REPORT).into()], self.file)?;
725        }
726        self.batch.flush(self.file)
727    }
728}
729impl Drop for EventWriter<'_> {
730    fn drop(&mut self) {
731        // If this is called after `finish`, `needs_syn_report` will be `false`, and the call to
732        // `self.batch.flush` will do nothing.
733        if let Err(e) = self.finish_impl() {
734            log::error!("uncaught error in `EventWriter` destructor: {e}");
735        }
736    }
737}
738
739/// Writes events to a selected multitouch slot.
740///
741/// Returned by [`EventWriter::slot`].
742#[derive(Debug)]
743#[must_use = "must call `SlotWriter::finish_slot` to finish modifying this slot"]
744pub struct SlotWriter<'a>(EventWriter<'a>);
745
746impl<'a> SlotWriter<'a> {
747    /// Sets the X and Y positions of this MT slot.
748    ///
749    /// This will emit [`Abs::MT_POSITION_X`] and [`Abs::MT_POSITION_Y`] events.
750    pub fn set_position(mut self, x: i32, y: i32) -> io::Result<Self> {
751        self.0 = self.0.write(&[
752            AbsEvent::new(Abs::MT_POSITION_X, x).into(),
753            AbsEvent::new(Abs::MT_POSITION_Y, y).into(),
754        ])?;
755        Ok(self)
756    }
757
758    /// Set the tracking ID of this MT slot.
759    pub fn set_tracking_id(mut self, id: i32) -> io::Result<Self> {
760        self.0 = self
761            .0
762            .write(&[AbsEvent::new(Abs::MT_TRACKING_ID, id).into()])?;
763        Ok(self)
764    }
765
766    /// Write raw events to the device.
767    ///
768    /// Any `ABS_MT_*` events will be associated with this MT slot.
769    pub fn write(mut self, events: &[InputEvent]) -> io::Result<Self> {
770        self.0 = self.0.write(events)?;
771        Ok(self)
772    }
773
774    /// Finishes updating this multitouch slot and returns the original [`EventWriter`].
775    pub fn finish_slot(self) -> io::Result<EventWriter<'a>> {
776        Ok(self.0)
777    }
778}
779
780/// An iterator over the events received by a [`UinputDevice`].
781///
782/// If the [`UinputDevice`] is in non-blocking mode, this iterator will end when there are no more
783/// events to read without blocking.
784/// Otherwise, iteration will block until more events are available.
785///
786/// [`UinputDevice::try_clone`] may be used to create multiple handles to the same device, so that
787/// one thread can read events while another writes them.
788#[derive(Debug)]
789pub struct Events<'a> {
790    dev: &'a UinputDevice,
791}
792
793impl Iterator for Events<'_> {
794    type Item = io::Result<InputEvent>;
795
796    fn next(&mut self) -> Option<Self::Item> {
797        unsafe {
798            let mut dest = MaybeUninit::<InputEvent>::uninit();
799            let bptr = dest.as_mut_ptr().cast::<u8>();
800            let byte_buf = slice::from_raw_parts_mut(bptr, mem::size_of::<InputEvent>());
801            let bytes = match (&self.dev.file).read(byte_buf) {
802                Ok(bytes) => bytes,
803                Err(e) if e.kind() == io::ErrorKind::WouldBlock => return None,
804                Err(e) => return Some(Err(e)),
805            };
806            assert_eq!(bytes, mem::size_of::<InputEvent>());
807            Some(Ok(dest.assume_init()))
808        }
809    }
810}
811
812/// Contains data about a force-feedback effect upload or update.
813///
814/// See [`UinputDevice::ff_upload`].
815#[repr(transparent)]
816pub struct ForceFeedbackUpload(uinput_ff_upload);
817
818impl ForceFeedbackUpload {
819    /// Returns the [`Effect`] that is being uploaded.
820    pub fn effect(&self) -> &Effect<'static> {
821        // Safety: `#[repr(transparent)]`
822        unsafe { mem::transmute::<&ff_effect, &Effect>(&self.0.effect) }
823    }
824
825    /// Returns the [`EffectId`] the input system has assigned to this force-feedback effect.
826    ///
827    /// This ID is referenced by force-feedback trigger events and by [`ForceFeedbackErase`]
828    /// commands, so implementations should store this somewhere.
829    pub fn effect_id(&self) -> EffectId {
830        self.effect().id()
831    }
832
833    /// If this upload overwrites an existing [`Effect`], this returns that effect.
834    ///
835    /// If this upload is uploading a *new* [`Effect`], this will refer to an invalid [`Effect`]
836    /// structure (likely with all fields zeroed out).
837    pub fn old(&self) -> &Effect<'static> {
838        // Safety: `#[repr(transparent)]`
839        unsafe { mem::transmute::<&ff_effect, &Effect>(&self.0.old) }
840    }
841}
842
843impl fmt::Debug for ForceFeedbackUpload {
844    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
845        f.debug_struct("ForceFeedbackUpload")
846            .field("request_id", &self.0.request_id)
847            .field("effect", self.effect())
848            .field("old", self.old())
849            .finish()
850    }
851}
852
853/// Contains data about a force-feedback effect deletion.
854///
855/// See [`UinputDevice::ff_erase`].
856#[repr(transparent)]
857pub struct ForceFeedbackErase(uinput_ff_erase);
858
859impl ForceFeedbackErase {
860    pub fn effect_id(&self) -> EffectId {
861        EffectId(self.0.effect_id.try_into().unwrap())
862    }
863}
864
865impl fmt::Debug for ForceFeedbackErase {
866    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
867        f.debug_struct("ForceFeedbackErase")
868            .field("request_id", &self.0.request_id)
869            .field("effect_id", &self.effect_id())
870            .finish()
871    }
872}