# denise-evdev
[](https://crates.io/crates/denise-evdev)
[](https://docs.rs/denise-evdev)
[](https://github.com/bisand/denise/blob/main/LICENSE)
Linux evdev input for **[Denise]**, a direct-rendering UI toolkit in Rust for
embedded Linux and systems without a desktop environment.
Reads mice, touchpads, touchscreens and keyboards straight from `/dev/input/event*`,
with **no display server in the way**, and turns them into `denise::InputEvent`s.
```rust
# #[cfg(target_os = "linux")]
# fn demo() -> Result<(), Box<dyn std::error::Error>> {
use denise::{InputSource, Size};
use denise_evdev::InputBackend;
// The surface size is what absolute touch coordinates get scaled into.
let mut input = InputBackend::open_all(Size::new(1280, 800))?;
let mut events = Vec::new();
input.poll(&mut events); // never blocks
# Ok(())
# }
```
## Blocking, and how not to spin
`poll` drains whatever is ready and returns. A frame loop that wants to sleep
should wait on `InputBackend::raw_fds` together with the DRM device's descriptor,
so the process idles in the kernel until either input arrives or the display
retires a flip — rather than waking up to ask.
That list changes under you. A wireless mouse that was asleep at startup has no
device node until somebody moves it, and `poll` opens it when it appears — so ask
`devices_changed()` each pass and take `raw_fds` again when it says yes. Holding
the first list forever is how a panel ends up with a mouse it can see in
`/dev/input` and cannot read.
## Touchpads
A touchpad reports the same multitouch slots as a touchscreen and is not one:
where a finger is on the pad says nothing about where on the screen it points.
Read as a touchscreen, a small laptop's pad touches the corner of the screen its
finger is in the corner of, and the pointer never moves. So a device with a finger
tool that the kernel has not marked as a screen (`INPUT_PROP_DIRECT`) is read as a
touchpad, which `capabilities()` reports:
- one finger moves the pointer by how far it travels, the pad's full width taking
it once across the surface, with fractions of a pixel carried rather than lost;
- two fingers scroll the way a wheel does;
- the pad's own button is a left button, and a right one with two fingers down;
- a short tap that barely moves is a left click, and a right click with two
fingers.
Taps need to know when events happened. `InputBackend` hands the translator the
kernel's timestamps; a `Translator` fed without them (`feed` rather than
`feed_at`) never guesses at one. A pen tablet reports a pen, not a finger, and
stays an absolute pointer.
## Keyboards
Key positions are translated to `KeyCode`, then composed into text: dead keys,
AltGr, and the modifier state that decides both. The tables live in
[`denise-layout`](https://crates.io/crates/denise-layout), which ships three —
**US**, **Norwegian** and **German** — and reads which one the machine is
configured for. The non-US AltGr assignments are a careful reconstruction and
want checking against real hardware.
There is also a console guard: on a bare VT the kernel is still echoing every
keystroke behind the panel, so `console` puts the tty into a raw, graphics mode for
the life of the process and restores it afterwards — including on panic, which is
the case that leaves a machine unusable if it is forgotten.
## Testing
`translate` and `keymap` are platform-independent and unit tested everywhere. That
is not tidiness: multitouch slot tracking, frame batching and modifier state are
the parts that break, and each is far easier to pin down as a table of raw event
codes than by dragging a finger across a panel and guessing. Only device discovery
and reading are gated to Linux.
Three examples help on a new board: `input` prints translated events, `keys` shows
what a keyboard produces layout by layout, and `pointer` tracks a cursor.
## Permissions
Reading `/dev/input/event*` needs membership in the `input` group, or root. Being
able to read every keystroke on the machine is exactly as sensitive as it sounds,
which is why the group exists.
## Platform
Linux only; elsewhere the crate compiles to almost nothing. `unsafe` is permitted
here and every block carries a `// SAFETY:` comment.
## Where this sits
Implements `denise::InputSource`. Pair it with
[`denise-drm`](https://crates.io/crates/denise-drm) or
[`denise-fbdev`](https://crates.io/crates/denise-fbdev) for output.
## Status
**M2 complete**, with touch routing exercised in unit tests and on a VM; no
physical touchscreen has confirmed it yet. Touchpads are unit tested, and an Asus
E200HA's I2C pad is recognised as one. Part of [Denise][Denise] — see the
[repository README][Denise] for the whole picture.
MIT licensed.
[Denise]: https://github.com/bisand/denise