Skip to main content
RKTK API Docs RKTK Home Repo

kmsm/state/
hid_report.rs

1use heapless::Vec;
2use usbd_hid::descriptor::{KeyboardReport, MediaKeyboardReport, MouseReport};
3
4use crate::{
5    interface::state::{
6        KeymapInfo,
7        input_event::InputEvent,
8        output_event::{EventType, OutputEvent},
9    },
10    keycode::KeyCode,
11};
12
13#[derive(Debug, PartialEq, Clone)]
14pub struct Report {
15    pub keyboard_report: Option<KeyboardReport>,
16    pub mouse_report: Option<MouseReport>,
17    pub media_keyboard_report: Option<MediaKeyboardReport>,
18    pub highest_layer: u8,
19}
20
21pub struct HidReportState<
22    const LAYER: usize,
23    const ROW: usize,
24    const COL: usize,
25    const ENCODER_COUNT: usize,
26    const NORMAL_MAX_PRESSED_KEYS: usize,
27    const ONESHOT_STATE_SIZE: usize,
28    const TAP_DANCE_MAX_DEFINITIONS: usize,
29    const TAP_DANCE_MAX_REPEATS: usize,
30    const COMBO_KEY_MAX_DEFINITIONS: usize,
31    const COMBO_KEY_MAX_SOURCES: usize,
32> {
33    state: super::State<
34        LAYER,
35        ROW,
36        COL,
37        ENCODER_COUNT,
38        NORMAL_MAX_PRESSED_KEYS,
39        ONESHOT_STATE_SIZE,
40        TAP_DANCE_MAX_DEFINITIONS,
41        TAP_DANCE_MAX_REPEATS,
42        COMBO_KEY_MAX_DEFINITIONS,
43        COMBO_KEY_MAX_SOURCES,
44    >,
45    next_send_keyboard_report: bool,
46    next_send_mkb_report: bool,
47}
48
49impl<
50    const LAYER: usize,
51    const ROW: usize,
52    const COL: usize,
53    const ENCODER_COUNT: usize,
54    const NORMAL_MAX_PRESSED_KEYS: usize,
55    const ONESHOT_STATE_SIZE: usize,
56    const TAP_DANCE_MAX_DEFINITIONS: usize,
57    const TAP_DANCE_MAX_REPEATS: usize,
58    const COMBO_KEY_MAX_DEFINITIONS: usize,
59    const COMBO_KEY_MAX_SOURCES: usize,
60>
61    HidReportState<
62        LAYER,
63        ROW,
64        COL,
65        ENCODER_COUNT,
66        NORMAL_MAX_PRESSED_KEYS,
67        ONESHOT_STATE_SIZE,
68        TAP_DANCE_MAX_DEFINITIONS,
69        TAP_DANCE_MAX_REPEATS,
70        COMBO_KEY_MAX_DEFINITIONS,
71        COMBO_KEY_MAX_SOURCES,
72    >
73{
74    pub fn new(
75        keymap: crate::keymap::Keymap<
76            LAYER,
77            ROW,
78            COL,
79            ENCODER_COUNT,
80            TAP_DANCE_MAX_DEFINITIONS,
81            TAP_DANCE_MAX_REPEATS,
82            COMBO_KEY_MAX_DEFINITIONS,
83            COMBO_KEY_MAX_SOURCES,
84        >,
85        config: crate::interface::state::config::StateConfig,
86    ) -> Self {
87        Self {
88            state: super::State::new(keymap, config),
89            next_send_keyboard_report: false,
90            next_send_mkb_report: false,
91        }
92    }
93
94    pub fn update(&mut self, event: InputEvent, since_last_update: core::time::Duration) -> Report {
95        self.update_with_cb(event, since_last_update, |_| {})
96    }
97
98    pub fn update_with_cb(
99        &mut self,
100        event: InputEvent,
101        since_last_update: core::time::Duration,
102        mut cb: impl FnMut(OutputEvent),
103    ) -> Report {
104        let mut keyboard_change = self.next_send_keyboard_report;
105        self.next_send_keyboard_report = false;
106        let mut keys: Vec<u8, 6> = Vec::new();
107        let mut modifier = 0u8;
108
109        let mut mouse_change = false;
110        let mut movement = (0, 0);
111        let mut scroll = (0, 0);
112        let mut mouse_buttons = 0u8;
113
114        let mut media_keyboard_change = self.next_send_mkb_report;
115        let mut media_keys = 0u16;
116
117        self.state.update(event, since_last_update, |ev| {
118            match ev {
119                OutputEvent::KeyCode((kc, ev)) => {
120                    match kc {
121                        KeyCode::Key(key) => {
122                            if ev != EventType::Pressing {
123                                keyboard_change = true;
124                            }
125                            if ev != EventType::Released && !keys.contains(&(key as u8)) {
126                                let _ = keys.push(key as u8);
127                            }
128                            // If both `Pressing` and `Released` events are sent same time, that means key
129                            // should be released in next report.
130                            if ev == EventType::Released && keys.contains(&(key as u8)) {
131                                self.next_send_keyboard_report = true;
132                            }
133                        }
134                        KeyCode::Media(m) => {
135                            if ev != EventType::Pressing {
136                                media_keyboard_change = true;
137                            }
138                            if ev != EventType::Released {
139                                media_keys = m as u16;
140                            }
141                            if ev == EventType::Released && media_keys == m as u16 {
142                                self.next_send_mkb_report = true;
143                            }
144                        }
145                        KeyCode::Modifier(m) => {
146                            if ev != EventType::Pressing {
147                                keyboard_change = true;
148                            }
149                            if ev != EventType::Released {
150                                modifier |= m as u8;
151                            }
152                        }
153                        KeyCode::Mouse(m) => {
154                            if ev != EventType::Pressing {
155                                mouse_change = true;
156                            }
157                            if ev != EventType::Released {
158                                mouse_buttons |= m as u8;
159                            }
160                        }
161                        // Other types of key codes will not appear as the HID report.
162                        _ => {}
163                    }
164                }
165                OutputEvent::MouseMove(m) => {
166                    mouse_change = true;
167                    movement.0 += m.0;
168                    movement.1 += m.1;
169                }
170                OutputEvent::MouseScroll((pan, wheel)) => {
171                    mouse_change = true;
172                    scroll.0 += pan;
173                    scroll.1 += wheel;
174                }
175            }
176            cb(ev);
177        });
178
179        Report {
180            keyboard_report: if keyboard_change {
181                keys.resize_default(6).unwrap();
182                let keycodes = keys.into_array().unwrap();
183                Some(KeyboardReport { keycodes, modifier, leds: 0, reserved: 0 })
184            } else {
185                None
186            },
187            mouse_report: if mouse_change {
188                Some(MouseReport {
189                    buttons: mouse_buttons,
190                    x: movement.0,
191                    y: movement.1,
192                    pan: scroll.0,
193                    wheel: scroll.1,
194                })
195            } else {
196                None
197            },
198            media_keyboard_report: if media_keyboard_change {
199                Some(MediaKeyboardReport { usage_id: media_keys })
200            } else {
201                None
202            },
203            highest_layer: self.state.shared.highest_layer() as u8,
204        }
205    }
206
207    pub fn inner(
208        &self,
209    ) -> &super::State<
210        LAYER,
211        ROW,
212        COL,
213        ENCODER_COUNT,
214        NORMAL_MAX_PRESSED_KEYS,
215        ONESHOT_STATE_SIZE,
216        TAP_DANCE_MAX_DEFINITIONS,
217        TAP_DANCE_MAX_REPEATS,
218        COMBO_KEY_MAX_DEFINITIONS,
219        COMBO_KEY_MAX_SOURCES,
220    > {
221        &self.state
222    }
223
224    pub fn get_keymap_info() -> KeymapInfo {
225        KeymapInfo {
226            layer_count: LAYER as u8,
227            max_tap_dance_key_count: TAP_DANCE_MAX_DEFINITIONS as u8,
228            max_tap_dance_repeat_count: TAP_DANCE_MAX_REPEATS as u8,
229            oneshot_state_size: ONESHOT_STATE_SIZE as u8,
230        }
231    }
232}