Skip to content

Commit e243eab

Browse files
committed
docs: extend the uinput module docs with examples
1 parent 5d91c60 commit e243eab

2 files changed

Lines changed: 124 additions & 1 deletion

File tree

src/evdev.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -592,7 +592,7 @@ impl Evdev {
592592
/// Revokes device access from this [`Evdev`] handle.
593593
///
594594
/// This prevents this handle from receiving any more input events, and makes writes and ioctls
595-
/// (including later calls to this one) fail with `ENODEV`.
595+
/// (including later calls to `revoke()`) fail with `ENODEV`.
596596
#[doc(alias = "EVIOCREVOKE")]
597597
pub fn revoke(&self) -> io::Result<()> {
598598
unsafe {

src/uinput.rs

Lines changed: 123 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,127 @@
44
//!
55
//! A [`UinputDevice`] can be created via [`UinputDevice::builder`] and will create a corresponding
66
//! evdev input device that other applications (or *this* application) can read events from.
7+
//!
8+
//! # Sending Events
9+
//!
10+
//! At its core, a uinput device obtains input events from some device, and then sends them to the
11+
//! kernel using [`UinputDevice::write`].
12+
//!
13+
//! Here is a simple example that presses and releases a keyboard key:
14+
//!
15+
//! ```
16+
//! use evdevil::{event::{Key, KeyEvent, KeyState}, uinput::UinputDevice};
17+
//!
18+
//! let dev = UinputDevice::builder()?
19+
//! .with_keys([Key::KEY_Q])?
20+
//! .build("My Input Device")?;
21+
//!
22+
//! dev.write(&[KeyEvent::new(Key::KEY_Q, KeyState::PRESSED).into()])?;
23+
//! dev.write(&[KeyEvent::new(Key::KEY_Q, KeyState::RELEASED).into()])?;
24+
//! # std::io::Result::Ok(())
25+
//! ```
26+
//!
27+
//! Other types of controls such as axes are implemented similarly: register the axis using
28+
//! [`Builder::with_rel_axes`] or [`Builder::with_abs_axes`], and send events using
29+
//! [`UinputDevice::write`].
30+
//!
31+
//! # Receiving Events
32+
//!
33+
//! Device features that are initiated by userspace are implemented by reading events *from* the
34+
//! [`UinputDevice`], using either [`UinputDevice::events`] or [`UinputDevice::read_events`].
35+
//!
36+
//! Here is a simple example device with an LED that can be controlled from userspace:
37+
//!
38+
//! ```
39+
//! use evdevil::{event::{Key, Led, KeyEvent, KeyState, EventKind}, uinput::UinputDevice};
40+
//!
41+
//! let dev = UinputDevice::builder()?
42+
//! .with_keys([Key::KEY_Q])?
43+
//! .with_leds([Led::CAPSL])?
44+
//! .build("My LED Device")?;
45+
//!
46+
//! dev.write(&[KeyEvent::new(Key::KEY_Q, KeyState::PRESSED).into()])?;
47+
//! dev.write(&[KeyEvent::new(Key::KEY_Q, KeyState::RELEASED).into()])?;
48+
//!
49+
//! // In another thread:
50+
//! # dev.set_nonblocking(true)?; // Make sure the test exits
51+
//! for res in dev.events() {
52+
//! let event = res?;
53+
//! println!("Received event: {event:?}");
54+
//! match event.kind() {
55+
//! EventKind::Led(ev) => {
56+
//! println!("LED {:?} set to {}", ev.led(), if ev.is_on() { "on" } else { "off" });
57+
//! },
58+
//! _ => {}
59+
//! }
60+
//! }
61+
//! # std::io::Result::Ok(())
62+
//! ```
63+
//!
64+
//! # Force-Feedback
65+
//!
66+
//! In evdev, force-feedback effects are used in two stages.
67+
//!
68+
//! 1. An effect is uploaded to the device using [`Evdev::upload_ff_effect`], yielding an
69+
//! [`EffectId`] that identifies the uploaded effect.
70+
//! 2. The effect is started and stopped by sending a [`ForceFeedbackEvent`] to the device,
71+
//! containing the [`EffectId`] (this can also be done by calling [`Evdev::control_ff`]).
72+
//!
73+
//! The uinput interface mirrors this approach.
74+
//!
75+
//! 1. When an evdev user requests an effect to be uploaded, a [`UinputEvent`] is enqueued for the
76+
//! [`UinputDevice`]. The uinput driver reads the event and calls [`UinputDevice::ff_upload`] to
77+
//! finish the upload.
78+
//! 2. When userspace starts or stops the effect, the [`UinputDevice`] receives the
79+
//! [`ForceFeedbackEvent`] and acts accordingly.
80+
//!
81+
//! For this to work, [`Builder::with_ff_features`] and [`Builder::with_ff_effects_max`]
82+
//! have to be used to advertise the force-feedback support to other programs when creating the
83+
//! device.
84+
//!
85+
//! A uinput device that supports force-feedback effects and exposes an LED that can be controlled:
86+
//!
87+
//! ```
88+
//! use evdevil::event::{Rel, EventKind, Led, ForceFeedbackCode, UinputCode};
89+
//! use evdevil::{ff, uinput::UinputDevice};
90+
//!
91+
//! let dev = UinputDevice::builder()?
92+
//! .with_rel_axes([Rel::DIAL])?
93+
//! .with_ff_features([ff::Feature::RUMBLE])?
94+
//! .with_ff_effects_max(5)?
95+
//! .build("Rusty Rumbler")?;
96+
//! # dev.set_nonblocking(true)?; // Make sure the test exits
97+
//!
98+
//! for res in dev.events() {
99+
//! let event = res?;
100+
//! println!("Received event: {event:?}");
101+
//! match event.kind() {
102+
//! EventKind::Uinput(ev) => match ev.code() {
103+
//! UinputCode::FF_UPLOAD => dev.ff_upload(&ev, |upl| {
104+
//! println!("Force-Feedback upload: {upl:?}");
105+
//! Ok(())
106+
//! })?,
107+
//! UinputCode::FF_ERASE => dev.ff_erase(&ev, |erase| {
108+
//! println!("Force-Feedback erase: {erase:?}");
109+
//! Ok(())
110+
//! })?,
111+
//! _ => {}
112+
//! },
113+
//! EventKind::ForceFeedback(ev) => match ev.code() {
114+
//! Some(ForceFeedbackCode::ControlEffect(id)) => {
115+
//! println!("FF effect {id:?} set to {}", ev.raw_value());
116+
//! },
117+
//! _ => {}
118+
//! },
119+
//! _ => {}
120+
//! }
121+
//! }
122+
//! # std::io::Result::Ok(())
123+
//! ```
124+
//!
125+
//! [`Evdev::upload_ff_effect`]: crate::Evdev::upload_ff_effect
126+
//! [`Evdev::control_ff`]: crate::Evdev::control_ff
127+
//! [`ForceFeedbackEvent`]: crate::event::ForceFeedbackEvent
7128
8129
use std::{
9130
error::Error,
@@ -381,6 +502,8 @@ impl Builder {
381502
}
382503

383504
/// A virtual `uinput` device.
505+
///
506+
/// Please refer to the [module documentation][self] for more information on how to use this.
384507
#[derive(Debug)]
385508
pub struct UinputDevice {
386509
// NOTE: we deliberately don't call `UI_DEV_DESTROY` on drop, since there can be multiple

0 commit comments

Comments
 (0)