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}