Skip to main content

keys/
keys.rs

1//! Shows what every key press produces: position, then character.
2//!
3//! The diagnostic for "my keyboard types the wrong thing". Each key reports the
4//! [`KeyCode`](denise::KeyCode) — the *position*, named after the US layout — and
5//! any text the layout composed from it is printed underneath, indented. A wrong
6//! layout and a wrong key are then immediately distinguishable: the position is a
7//! fact about the hardware, the text is a fact about the layout.
8//!
9//! Read-only and display-free: it touches no DRM device and sets no mode, so it is
10//! safe to run over SSH while looking at the console.
11//!
12//! ```text
13//! cargo run -p denise-evdev --example keys -- [seconds] [layout]
14//! /tmp/keys 30 no
15//! ```
16//!
17//! A dead key prints a position and no text at all until the next key resolves it.
18//! That is correct, and it is also exactly what a broken keyboard looks like if
19//! you are not expecting it.
20
21#[cfg(not(target_os = "linux"))]
22fn main() {
23    eprintln!("this needs Linux and evdev");
24}
25
26#[cfg(target_os = "linux")]
27fn main() -> Result<(), Box<dyn std::error::Error>> {
28    use std::time::{Duration, Instant};
29
30    use denise::{ElementState, InputEvent, InputSource, KeyCode, Modifiers, Size};
31    use denise_evdev::{InputBackend, layout};
32
33    let seconds: u64 = std::env::args()
34        .nth(1)
35        .and_then(|a| a.parse().ok())
36        .unwrap_or(30)
37        .clamp(1, 600);
38
39    let mut input = InputBackend::open_all(Size::new(1280, 800))?;
40    let (chosen, source) = match std::env::args().nth(2) {
41        Some(name) => match layout::by_name(layout::normalise_name(&name)) {
42            Some(layout) => {
43                input.set_layout(layout);
44                (layout, layout::LayoutSource::Denise)
45            }
46            None => {
47                eprintln!("no layout called {name:?}; falling back to the system's");
48                input.set_layout_from_system()
49            }
50        },
51        None => input.set_layout_from_system(),
52    };
53
54    for device in input.devices() {
55        eprintln!("input   {}: {}", device.capabilities(), device.name());
56    }
57    eprintln!(
58        "keymap  {} (from {source})   (available: {})",
59        chosen.name,
60        layout::BUILT_IN
61            .iter()
62            .map(|l| l.name)
63            .collect::<Vec<_>>()
64            .join(", ")
65    );
66    eprintln!("\npress keys — Escape quits\n");
67
68    let deadline = Instant::now() + Duration::from_secs(seconds);
69    let mut events = Vec::new();
70    let mut keys = 0u32;
71    let mut characters = 0u32;
72    let mut quit = false;
73
74    while !quit && Instant::now() < deadline {
75        events.clear();
76        input.poll(&mut events);
77
78        for event in &events {
79            match event {
80                InputEvent::Key {
81                    code,
82                    state: ElementState::Down,
83                    modifiers,
84                    repeat,
85                } => {
86                    keys += 1;
87                    let mut held = String::new();
88                    for (bit, name) in [
89                        (Modifiers::SHIFT, "shift"),
90                        (Modifiers::CTRL, "ctrl"),
91                        (Modifiers::ALT, "alt"),
92                        (Modifiers::SUPER, "super"),
93                    ] {
94                        if modifiers.contains(bit) {
95                            held.push(' ');
96                            held.push_str(name);
97                        }
98                    }
99                    let repeat = if *repeat { " (repeat)" } else { "" };
100                    eprintln!("key   {code:?}{held}{repeat}");
101                    if *code == KeyCode::Escape {
102                        quit = true;
103                    }
104                }
105                InputEvent::Text { ch } => {
106                    characters += 1;
107                    eprintln!("  --> text {ch:?}  U+{:04X}", *ch as u32);
108                }
109                _ => {}
110            }
111        }
112        std::thread::sleep(Duration::from_millis(8));
113    }
114
115    eprintln!("\n{keys} key presses produced {characters} characters");
116    if keys > 0 && characters == 0 {
117        eprintln!("nothing typed: either the keys pressed are not text keys, or");
118        eprintln!("the layout above is not the one on the keyboard");
119    }
120    Ok(())
121}