Skip to main content

starnix_modules_input_report_event_conversion/
keyboard.rs

1// Copyright 2026 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5use fidl_fuchsia_input_report as fir;
6use sorted_vec_map::SortedVecSet;
7use starnix_modules_input_app_event_conversion::keymap::KEY_MAP;
8use starnix_types::time::timeval_from_time;
9use starnix_uapi::uapi;
10
11/// Converts `KeyboardInputReport` events into Linux `uapi::input_event`s (EV_KEY press and release).
12#[derive(Debug, Default, PartialEq)]
13pub struct KeyboardReportConverter {
14    pressed_keys: SortedVecSet<u16>,
15}
16
17impl KeyboardReportConverter {
18    pub fn new() -> Self {
19        Self::default()
20    }
21
22    pub fn handle(
23        &mut self,
24        report: &fir::KeyboardInputReport,
25        event_time: zx::MonotonicInstant,
26    ) -> Vec<uapi::input_event> {
27        let mut events = Vec::new();
28        let time = timeval_from_time(event_time);
29
30        let pressed_keys3 = report.pressed_keys3.as_deref().unwrap_or(&[]);
31        let mut current_keys = SortedVecSet::new();
32        for &key in pressed_keys3.iter() {
33            let lkey = KEY_MAP.fuchsia_input_key_to_linux_keycode(key);
34            if lkey != uapi::KEY_RESERVED {
35                current_keys.insert(lkey as u16);
36            }
37        }
38
39        crate::emit_key_transitions(&mut self.pressed_keys, current_keys, time, &mut events);
40
41        if !events.is_empty() {
42            events.push(uapi::input_event {
43                time,
44                type_: uapi::EV_SYN as u16,
45                code: uapi::SYN_REPORT as u16,
46                value: 0,
47            });
48        }
49
50        events
51    }
52}
53
54#[cfg(test)]
55mod tests {
56    use super::*;
57    use fidl_fuchsia_input::Key;
58
59    #[::fuchsia::test]
60    fn test_keyboard_report_keys() {
61        let mut converter = KeyboardReportConverter::new();
62        let time = zx::MonotonicInstant::from_nanos(500_000_000);
63
64        // Press Key A
65        let report1 =
66            fir::KeyboardInputReport { pressed_keys3: Some(vec![Key::A]), ..Default::default() };
67        let events1 = converter.handle(&report1, time);
68        assert_eq!(
69            events1.iter().map(|e| (e.type_, e.code, e.value)).collect::<Vec<_>>(),
70            vec![
71                (uapi::EV_KEY as u16, uapi::KEY_A as u16, 1),
72                (uapi::EV_SYN as u16, uapi::SYN_REPORT as u16, 0),
73            ]
74        );
75
76        // Hold Key A and press Key B
77        let report2 = fir::KeyboardInputReport {
78            pressed_keys3: Some(vec![Key::A, Key::B]),
79            ..Default::default()
80        };
81        let events2 = converter.handle(&report2, time);
82        assert_eq!(
83            events2.iter().map(|e| (e.type_, e.code, e.value)).collect::<Vec<_>>(),
84            vec![
85                (uapi::EV_KEY as u16, uapi::KEY_B as u16, 1),
86                (uapi::EV_SYN as u16, uapi::SYN_REPORT as u16, 0),
87            ]
88        );
89
90        // Release Key A, hold Key B
91        let report3 =
92            fir::KeyboardInputReport { pressed_keys3: Some(vec![Key::B]), ..Default::default() };
93        let events3 = converter.handle(&report3, time);
94        assert_eq!(
95            events3.iter().map(|e| (e.type_, e.code, e.value)).collect::<Vec<_>>(),
96            vec![
97                (uapi::EV_KEY as u16, uapi::KEY_A as u16, 0),
98                (uapi::EV_SYN as u16, uapi::SYN_REPORT as u16, 0),
99            ]
100        );
101
102        // Release all
103        let report4 =
104            fir::KeyboardInputReport { pressed_keys3: Some(vec![]), ..Default::default() };
105        let events4 = converter.handle(&report4, time);
106        assert_eq!(
107            events4.iter().map(|e| (e.type_, e.code, e.value)).collect::<Vec<_>>(),
108            vec![
109                (uapi::EV_KEY as u16, uapi::KEY_B as u16, 0),
110                (uapi::EV_SYN as u16, uapi::SYN_REPORT as u16, 0),
111            ]
112        );
113    }
114
115    #[::fuchsia::test]
116    fn test_keyboard_report_unmapped_key_filtered() {
117        let mut converter = KeyboardReportConverter::new();
118        let time = zx::MonotonicInstant::from_nanos(300_000_000);
119
120        let report = fir::KeyboardInputReport {
121            pressed_keys3: Some(vec![Key::Unknown]),
122            ..Default::default()
123        };
124        let events = converter.handle(&report, time);
125        // Key::Unknown maps to KEY_RESERVED, which is filtered out, yielding no events
126        assert!(events.is_empty());
127    }
128
129    #[::fuchsia::test]
130    fn test_empty_keyboard_reports_return_empty_events() {
131        let time = zx::MonotonicInstant::from_nanos(400_000_000);
132        assert!(
133            KeyboardReportConverter::new()
134                .handle(&fir::KeyboardInputReport::default(), time)
135                .is_empty()
136        );
137    }
138
139    #[::fuchsia::test]
140    fn test_keyboard_report_none_pressed_keys_releases_held_keys() {
141        let mut converter = KeyboardReportConverter::new();
142        let time = zx::MonotonicInstant::from_nanos(400_000_000);
143
144        // Press Key A
145        let report1 =
146            fir::KeyboardInputReport { pressed_keys3: Some(vec![Key::A]), ..Default::default() };
147        let events1 = converter.handle(&report1, time);
148        assert_eq!(
149            events1.iter().map(|e| (e.type_, e.code, e.value)).collect::<Vec<_>>(),
150            vec![
151                (uapi::EV_KEY as u16, uapi::KEY_A as u16, 1),
152                (uapi::EV_SYN as u16, uapi::SYN_REPORT as u16, 0),
153            ]
154        );
155
156        // Subsequent report with pressed_keys3: None releases Key A
157        let report2 = fir::KeyboardInputReport { pressed_keys3: None, ..Default::default() };
158        let events2 = converter.handle(&report2, time);
159        assert_eq!(
160            events2.iter().map(|e| (e.type_, e.code, e.value)).collect::<Vec<_>>(),
161            vec![
162                (uapi::EV_KEY as u16, uapi::KEY_A as u16, 0),
163                (uapi::EV_SYN as u16, uapi::SYN_REPORT as u16, 0),
164            ]
165        );
166
167        // Subsequent report with pressed_keys3: None when no keys held emits nothing
168        let report3 = fir::KeyboardInputReport { pressed_keys3: None, ..Default::default() };
169        let events3 = converter.handle(&report3, time);
170        assert!(events3.is_empty());
171    }
172}