smart_keymap/keymap/
hid_keyboard_reporter.rs

1use core::fmt::Debug;
2
3use crate::key;
4
5use super::MAX_PRESSED_KEYS;
6
7/// Transforms output from the keymap so it's suitable for HID keyboard reports.
8///
9/// e.g. limits output to one new pressed key per sent report,
10///  so that the USB host doesn't confuse the sequence of pressed keys.
11#[derive(Debug)]
12pub struct HIDKeyboardReporter {
13    pressed_key_outputs: heapless::Vec<key::KeyOutput, { MAX_PRESSED_KEYS }>,
14    num_reportable_keys: u8,
15}
16
17impl Default for HIDKeyboardReporter {
18    fn default() -> Self {
19        Self::new()
20    }
21}
22
23impl HIDKeyboardReporter {
24    /// Constructs a new HIDKeyboardReporter.
25    pub const fn new() -> Self {
26        Self {
27            pressed_key_outputs: heapless::Vec::new(),
28            num_reportable_keys: 1,
29        }
30    }
31
32    /// Transforms the keymap output to a HID keyboard report.
33    pub fn init(&mut self) {
34        self.pressed_key_outputs.clear();
35        self.num_reportable_keys = 1;
36    }
37
38    /// Updates the state of the HIDKeyboardReporter with the given pressed key outputs.
39    pub fn update(
40        &mut self,
41        pressed_key_outputs: heapless::Vec<key::KeyOutput, { MAX_PRESSED_KEYS }>,
42    ) {
43        // e.g.
44        //  WAS: A B C
45        //  NOW: A   C D
46        //   -> released B, pressed D
47        let mut prev_iter = self.pressed_key_outputs.iter();
48        let new_iter = pressed_key_outputs.iter();
49
50        for new_key_output in new_iter {
51            for prev_key_output in prev_iter.by_ref() {
52                if prev_key_output == new_key_output {
53                    // Same key output in both
54                    break;
55                } else {
56                    // The key in the previous report doesn't match key in new report;
57                    //  hence, it has been released.
58                    if self.num_reportable_keys > 1 {
59                        self.num_reportable_keys -= 1;
60                    }
61                }
62            }
63        }
64
65        for _ in prev_iter {
66            // The key in the previous report, but not in new report.
67            //  hence, it has been released.
68            if self.num_reportable_keys > 1 {
69                self.num_reportable_keys -= 1;
70            }
71        }
72
73        self.pressed_key_outputs = pressed_key_outputs;
74    }
75
76    /// Indicate an HID report was sent. Allows reporting one more key in the next report.
77    pub fn report_sent(&mut self) {
78        if self.pressed_key_outputs.len() > self.num_reportable_keys.into() {
79            self.num_reportable_keys += 1;
80        }
81    }
82
83    /// Gets the filtered pressed key outputs, suitable for sending for HID reports.
84    pub fn reportable_key_outputs(&self) -> heapless::Vec<key::KeyOutput, { MAX_PRESSED_KEYS }> {
85        self.pressed_key_outputs
86            .clone()
87            .into_iter()
88            .take(self.num_reportable_keys as usize)
89            .collect()
90    }
91}
92
93#[cfg(test)]
94mod tests {
95    use super::super::*;
96    use super::*;
97
98    #[test]
99    fn test_hid_keyboard_reporter_reports_single_keypress() {
100        // Assemble
101        let mut input: heapless::Vec<key::KeyOutput, { MAX_PRESSED_KEYS }> = heapless::Vec::new();
102        input.push(key::KeyOutput::from_key_code(0x04)).unwrap();
103
104        let mut reporter = HIDKeyboardReporter::new();
105
106        // Act
107        reporter.update(input);
108        let actual_outputs = reporter.reportable_key_outputs();
109
110        // Assert
111        let expected_outputs: heapless::Vec<key::KeyOutput, { MAX_PRESSED_KEYS }> = [0x04]
112            .iter()
113            .map(|&kc| key::KeyOutput::from_key_code(kc))
114            .collect();
115        assert_eq!(expected_outputs, actual_outputs);
116    }
117
118    #[test]
119    fn test_hid_keyboard_reporter_reports_single_new_keypress_per_report_sent() {
120        // Assemble
121        let input: heapless::Vec<key::KeyOutput, { MAX_PRESSED_KEYS }> = [0x04, 0x05]
122            .iter()
123            .map(|&kc| key::KeyOutput::from_key_code(kc))
124            .collect();
125
126        let mut reporter = HIDKeyboardReporter::new();
127
128        // Act
129        reporter.update(input);
130        let actual_outputs = reporter.reportable_key_outputs();
131
132        // Assert
133        let expected_outputs: heapless::Vec<key::KeyOutput, { MAX_PRESSED_KEYS }> = [0x04]
134            .iter()
135            .map(|&kc| key::KeyOutput::from_key_code(kc))
136            .collect();
137        assert_eq!(expected_outputs, actual_outputs);
138    }
139
140    #[test]
141    fn test_hid_keyboard_reporter_reports_more_keypresses_after_report_sent() {
142        // Assemble
143        let input: heapless::Vec<key::KeyOutput, { MAX_PRESSED_KEYS }> = [0x04, 0x05]
144            .iter()
145            .map(|&kc| key::KeyOutput::from_key_code(kc))
146            .collect();
147
148        let mut reporter = HIDKeyboardReporter::new();
149
150        // Act
151        reporter.update(input);
152        reporter.report_sent();
153        let actual_outputs = reporter.reportable_key_outputs();
154
155        // Assert
156        let expected_outputs: heapless::Vec<key::KeyOutput, { MAX_PRESSED_KEYS }> = [0x04, 0x05]
157            .iter()
158            .map(|&kc| key::KeyOutput::from_key_code(kc))
159            .collect();
160        assert_eq!(expected_outputs, actual_outputs);
161    }
162
163    #[test]
164    fn test_hid_keyboard_reporter_reports_updates_for_key_releases() {
165        // Assemble
166        let input: heapless::Vec<key::KeyOutput, { MAX_PRESSED_KEYS }> = [0x04, 0x05]
167            .iter()
168            .map(|&kc| key::KeyOutput::from_key_code(kc))
169            .collect();
170        let input_after_key_released: heapless::Vec<key::KeyOutput, { MAX_PRESSED_KEYS }> = [0x05]
171            .iter()
172            .map(|&kc| key::KeyOutput::from_key_code(kc))
173            .collect();
174        let input_after_more_keys_pressed: heapless::Vec<key::KeyOutput, { MAX_PRESSED_KEYS }> =
175            [0x05, 0x06, 0x07]
176                .iter()
177                .map(|&kc| key::KeyOutput::from_key_code(kc))
178                .collect();
179
180        let mut reporter = HIDKeyboardReporter::new();
181
182        // Act
183        reporter.update(input);
184        reporter.report_sent(); // now may report 2 keys
185        assert_eq!(2, reporter.num_reportable_keys);
186        reporter.update(input_after_key_released); // 1 key released; so, only may report 1 key
187        assert_eq!(1, reporter.num_reportable_keys);
188        reporter.report_sent();
189        assert_eq!(1, reporter.num_reportable_keys);
190        reporter.update(input_after_more_keys_pressed); // 1+2 new pressed in KM; only 2 should reported
191        reporter.report_sent();
192        assert_eq!(2, reporter.num_reportable_keys);
193        let actual_outputs = reporter.reportable_key_outputs();
194
195        // Assert
196        let expected_outputs: heapless::Vec<key::KeyOutput, { MAX_PRESSED_KEYS }> = [0x05, 0x06]
197            .iter()
198            .map(|&kc| key::KeyOutput::from_key_code(kc))
199            .collect();
200        assert_eq!(
201            KeymapOutput::new(expected_outputs).as_hid_boot_keyboard_report(),
202            KeymapOutput::new(actual_outputs).as_hid_boot_keyboard_report(),
203        );
204    }
205}