Skip to main content

ferroui_linuxframebuffer/input/ev_dev/
ev_dev_keyboard.rs

1//! The handler of a keyboard.
2//!
3//! An addition (docs/porting/linux-framebuffer-platform.md, section 7):
4//! the reference reads no keys. The keys are those of a US keyboard: no
5//! layout is read from the system.
6
7use super::ev_dev_key_table::physical_key_from_ev_key;
8use super::{EvDevDeviceHandler, EvDevRawEvent};
9use crate::native_unsafe_methods::{ev_type, input_event};
10use ferroui_base::input::raw::RawKeyEventType;
11use ferroui_base::input::{PhysicalKey, RawInputModifiers};
12use ferroui_base::Size;
13
14/// The description of a keyboard: `(path):type=keyboard`.
15#[derive(Clone, Debug, Default, PartialEq)]
16pub struct EvDevKeyboardDeviceDescription {
17    pub path: Option<String>,
18}
19
20#[derive(Default)]
21pub struct EvDevKeyboard {
22    /// The modifier keys that are held.
23    held: Vec<PhysicalKey>,
24    caps_lock: bool,
25}
26
27fn modifier_of(key: PhysicalKey) -> Option<RawInputModifiers> {
28    match key {
29        PhysicalKey::AltLeft | PhysicalKey::AltRight => Some(RawInputModifiers::ALT),
30        PhysicalKey::ControlLeft | PhysicalKey::ControlRight => Some(RawInputModifiers::CONTROL),
31        PhysicalKey::ShiftLeft | PhysicalKey::ShiftRight => Some(RawInputModifiers::SHIFT),
32        PhysicalKey::MetaLeft | PhysicalKey::MetaRight => Some(RawInputModifiers::META),
33        _ => None,
34    }
35}
36
37impl EvDevKeyboard {
38    pub fn new() -> Self {
39        Self::default()
40    }
41
42    fn modifiers(&self) -> RawInputModifiers {
43        self.held.iter().filter_map(|&key| modifier_of(key)).fold(RawInputModifiers::empty(), |all, one| all | one)
44    }
45}
46
47impl EvDevDeviceHandler for EvDevKeyboard {
48    fn handle_event(&mut self, ev: &input_event, _screen_size: Size, out: &mut Vec<EvDevRawEvent>) {
49        if ev.type_ != ev_type::EV_KEY {
50            return;
51        }
52        let physical_key = physical_key_from_ev_key(ev.code);
53        if physical_key == PhysicalKey::None {
54            return;
55        }
56
57        // 1 is a press, 2 a repeat of a held key, 0 a release.
58        let down = ev.value != 0;
59        if modifier_of(physical_key).is_some() {
60            self.held.retain(|&held| held != physical_key);
61            if down {
62                self.held.push(physical_key);
63            }
64        }
65        if physical_key == PhysicalKey::CapsLock && ev.value == 1 {
66            self.caps_lock = !self.caps_lock;
67        }
68
69        let modifiers = self.modifiers();
70        let shift = modifiers.contains(RawInputModifiers::SHIFT);
71        let is_letter = (PhysicalKey::A as i32..=PhysicalKey::Z as i32).contains(&(physical_key as i32));
72        let key_symbol = physical_key.to_qwerty_key_symbol(shift != (self.caps_lock && is_letter));
73        let timestamp = ev.timestamp();
74
75        out.push(EvDevRawEvent::Key {
76            type_: if down { RawKeyEventType::KeyDown } else { RawKeyEventType::KeyUp },
77            timestamp,
78            key: physical_key.to_qwerty_key(),
79            modifiers,
80            physical_key,
81            key_symbol: key_symbol.map(str::to_owned),
82        });
83
84        let commands = RawInputModifiers::CONTROL | RawInputModifiers::ALT | RawInputModifiers::META;
85        if let Some(text) = key_symbol {
86            let printable = text.chars().all(|c| !c.is_control());
87            if down && printable && !modifiers.intersects(commands) {
88                out.push(EvDevRawEvent::Text { timestamp, text: text.to_owned() });
89            }
90        }
91    }
92
93    fn reset(&mut self, _timestamp: u64, _screen_size: Size, _out: &mut Vec<EvDevRawEvent>) {
94        self.held.clear();
95    }
96}
97
98#[cfg(test)]
99mod tests {
100    use super::super::ev_dev_touch_screen::tests::{ev, run, syn};
101    use super::*;
102    use ferroui_base::input::Key;
103
104    const KEY_A: u16 = 30;
105    const KEY_1: u16 = 2;
106    const KEY_ENTER: u16 = 28;
107    const KEY_LEFTSHIFT: u16 = 42;
108    const KEY_LEFTCTRL: u16 = 29;
109    const KEY_CAPSLOCK: u16 = 58;
110
111    fn key(code: u16, value: i32) -> input_event {
112        ev(10, ev_type::EV_KEY, code, value)
113    }
114
115    /// The events as `(down, key, modifiers, symbol)` and texts.
116    fn summary(events: &[input_event]) -> Vec<String> {
117        let mut keyboard = EvDevKeyboard::new();
118        run(&mut keyboard, events, Size::default())
119            .into_iter()
120            .map(|event| match event {
121                EvDevRawEvent::Key { type_, key, modifiers, key_symbol, .. } => {
122                    let direction = if type_ == RawKeyEventType::KeyDown { "down" } else { "up" };
123                    format!("{direction} {key:?} {} {:?}", modifiers.bits(), key_symbol.unwrap_or_default())
124                }
125                EvDevRawEvent::Text { text, .. } => format!("text {text:?}"),
126                other => panic!("unexpected {other:?}"),
127            })
128            .collect()
129    }
130
131    #[test]
132    fn a_key_is_a_down_with_its_text_and_an_up() {
133        // The scan code and the report that surround a key are passed over.
134        let events = [ev(10, ev_type::EV_MSC, 4, 0x70004), key(KEY_A, 1), syn(10), key(KEY_A, 0), syn(11)];
135        assert_eq!(summary(&events), ["down A 0 \"a\"", "text \"a\"", "up A 0 \"a\""]);
136    }
137
138    #[test]
139    fn a_held_key_repeats_as_further_downs() {
140        let events = [key(KEY_A, 1), key(KEY_A, 2), key(KEY_A, 2), key(KEY_A, 0)];
141        assert_eq!(
142            summary(&events),
143            [
144                "down A 0 \"a\"",
145                "text \"a\"",
146                "down A 0 \"a\"",
147                "text \"a\"",
148                "down A 0 \"a\"",
149                "text \"a\"",
150                "up A 0 \"a\""
151            ]
152        );
153    }
154
155    #[test]
156    fn shift_changes_the_symbol_and_is_a_modifier_while_held() {
157        let events = [key(KEY_LEFTSHIFT, 1), key(KEY_A, 1), key(KEY_1, 1), key(KEY_LEFTSHIFT, 0), key(KEY_A, 1)];
158        assert_eq!(
159            summary(&events),
160            [
161                "down LeftShift 4 \"\"",
162                "down A 4 \"A\"",
163                "text \"A\"",
164                "down D1 4 \"!\"",
165                "text \"!\"",
166                "up LeftShift 0 \"\"",
167                "down A 0 \"a\"",
168                "text \"a\"",
169            ]
170        );
171    }
172
173    #[test]
174    fn caps_lock_changes_letters_only_and_shift_undoes_it() {
175        let events = [
176            key(KEY_CAPSLOCK, 1),
177            key(KEY_CAPSLOCK, 0),
178            key(KEY_A, 1),
179            key(KEY_1, 1),
180            key(KEY_LEFTSHIFT, 1),
181            key(KEY_A, 1),
182        ];
183        let summary = summary(&events);
184        assert_eq!(
185            summary[2..],
186            [
187                "down A 0 \"A\"",
188                "text \"A\"",
189                "down D1 0 \"1\"",
190                "text \"1\"",
191                "down LeftShift 4 \"\"",
192                "down A 4 \"a\"",
193                "text \"a\""
194            ]
195        );
196    }
197
198    #[test]
199    fn a_key_with_control_and_a_key_without_a_printable_symbol_have_no_text() {
200        let events = [key(KEY_ENTER, 1), key(KEY_LEFTCTRL, 1), key(KEY_A, 1)];
201        assert_eq!(summary(&events), ["down Return 0 \"\\r\"", "down LeftCtrl 2 \"\"", "down A 2 \"a\""]);
202        assert_eq!(physical_key_from_ev_key(KEY_ENTER).to_qwerty_key(), Key::Return);
203    }
204}