ferroui_linuxframebuffer/input/ev_dev/
ev_dev_keyboard.rs1use 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#[derive(Clone, Debug, Default, PartialEq)]
16pub struct EvDevKeyboardDeviceDescription {
17 pub path: Option<String>,
18}
19
20#[derive(Default)]
21pub struct EvDevKeyboard {
22 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 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 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 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}