smart_keymap/
keymap.rs

1#[cfg(feature = "std")]
2mod distinct_reports;
3mod event_scheduler;
4/// The HID keyboard reporter.
5pub mod hid_keyboard_reporter;
6
7use core::cmp::PartialEq;
8use core::fmt::Debug;
9use core::marker::Copy;
10use core::ops::Index;
11
12use serde::Deserialize;
13
14use crate::input;
15use crate::key;
16
17use key::Event;
18
19#[cfg(feature = "std")]
20pub use distinct_reports::DistinctReports;
21use event_scheduler::EventScheduler;
22use hid_keyboard_reporter::HIDKeyboardReporter;
23
24/// Maximum number of pressed keys supported.
25pub const MAX_PRESSED_KEYS: usize = 16;
26
27const MAX_QUEUED_INPUT_EVENTS: usize = 32;
28
29/// Number of ticks before the next input event is processed in tick().
30pub const INPUT_QUEUE_TICK_DELAY: u8 = 1;
31
32/// Constructs an HID report or a sequence of key codes from the given sequence of [key::KeyOutput].
33#[derive(Debug, PartialEq)]
34pub struct KeymapOutput {
35    pressed_key_codes: heapless::Vec<key::KeyOutput, { MAX_PRESSED_KEYS }>,
36}
37
38impl Default for KeymapOutput {
39    fn default() -> Self {
40        Self {
41            pressed_key_codes: heapless::Vec::new(),
42        }
43    }
44}
45
46impl KeymapOutput {
47    /// Constructs a new keymap output.
48    pub fn new(pressed_key_codes: heapless::Vec<key::KeyOutput, { MAX_PRESSED_KEYS }>) -> Self {
49        Self { pressed_key_codes }
50    }
51
52    /// Returns the pressed key codes.
53    pub fn pressed_key_codes(&self) -> heapless::Vec<u8, 24> {
54        let mut result = heapless::Vec::new();
55
56        let modifiers = self
57            .pressed_key_codes
58            .iter()
59            .fold(key::KeyboardModifiers::new(), |acc, &ko| {
60                acc.union(&ko.key_modifiers())
61            });
62
63        result.extend(modifiers.as_key_codes());
64
65        result.extend(
66            self.pressed_key_codes
67                .iter()
68                .flat_map(|ko| match ko.key_code() {
69                    key::KeyUsage::Keyboard(kc) => Some(kc),
70                    _ => None,
71                }),
72        );
73
74        result
75    }
76
77    /// Returns the current HID keyboard report.
78    pub fn as_hid_boot_keyboard_report(&self) -> [u8; 8] {
79        let mut report = [0u8; 8];
80
81        let modifiers = self
82            .pressed_key_codes
83            .iter()
84            .fold(key::KeyboardModifiers::new(), |acc, &ko| {
85                acc.union(&ko.key_modifiers())
86            });
87
88        report[0] = modifiers.as_byte();
89
90        let key_codes = self
91            .pressed_key_codes
92            .iter()
93            .flat_map(|ko| match ko.key_code() {
94                key::KeyUsage::Keyboard(kc) => Some(kc),
95                _ => None,
96            })
97            .filter(|&kc| kc != 0);
98
99        for (i, key_code) in key_codes.take(6).enumerate() {
100            report[i + 2] = key_code;
101        }
102
103        report
104    }
105
106    /// Returns the pressed custom codes.
107    pub fn pressed_custom_codes(&self) -> heapless::Vec<u8, 24> {
108        self.pressed_key_codes
109            .iter()
110            .flat_map(|ko| match ko.key_code() {
111                key::KeyUsage::Custom(kc) => Some(kc),
112                _ => None,
113            })
114            .collect()
115    }
116}
117
118#[derive(Debug)]
119struct PendingState<R, Ev, PKS> {
120    keymap_index: u16,
121    key_ref: R,
122    pending_key_state: PKS,
123    queued_events: heapless::Vec<key::Event<Ev>, { MAX_PRESSED_KEYS }>,
124}
125
126/// Callbacks for effect keys in the keymap.
127#[derive(Deserialize, Debug, Clone, Copy, Eq, PartialEq)]
128pub enum KeymapCallback {
129    /// Reset the keyboard
130    Reset,
131    /// Reset the keyboard to bootloader
132    ResetToBootloader,
133    /// A custom callback. Its behaviour is specific to the firmware implementation.
134    Custom(u8, u8),
135}
136
137/// Context provided from the keymap to the smart keys.
138#[derive(Debug, Clone, Copy)]
139pub struct KeymapContext {
140    /// Number of milliseconds since keymap has been initialized.
141    pub time_ms: u32,
142
143    /// Number of milliseconds since keymap received an input event.
144    pub idle_time_ms: u32,
145}
146
147/// Default keymap context.
148pub const DEFAULT_KEYMAP_CONTEXT: KeymapContext = KeymapContext {
149    time_ms: 0,
150    idle_time_ms: 0,
151};
152
153/// Trait for setting the keymap context.
154pub trait SetKeymapContext {
155    /// Sets the keymap context.
156    fn set_keymap_context(&mut self, context: KeymapContext);
157}
158
159/// Events related to the keymap.
160#[derive(Debug, Clone, Copy, PartialEq, Eq)]
161pub enum KeymapEvent {
162    /// Callback event (emitted by callback key).
163    Callback(KeymapCallback),
164    /// A pressed key resolved to a state with this key output.
165    ResolvedKeyOutput {
166        /// The keymap index of the key which resolved to the output.
167        keymap_index: u16,
168        /// The resolved key output.
169        key_output: key::KeyOutput,
170    },
171}
172
173#[derive(Debug)]
174enum CallbackFunction {
175    /// C callback
176    ExternC(extern "C" fn() -> ()),
177    /// Rust callback
178    Rust(fn() -> ()),
179}
180
181/// State for a keymap that handles input, and outputs HID keyboard reports.
182pub struct Keymap<I: Index<usize, Output = R>, R, Ctx, Ev: Debug, PKS, KS, S> {
183    key_refs: I,
184    key_system: S,
185    context: Ctx,
186    pressed_inputs: heapless::Vec<input::PressedInput<R, KS>, { MAX_PRESSED_KEYS }>,
187    event_scheduler: EventScheduler<Ev>,
188    ms_per_tick: u8,
189    idle_time: u32,
190    hid_reporter: HIDKeyboardReporter,
191    pending_key_state: Option<PendingState<R, Ev, PKS>>,
192    input_queue: heapless::spsc::Queue<input::Event, { MAX_QUEUED_INPUT_EVENTS }>,
193    input_queue_delay_counter: u8,
194    callbacks: heapless::LinearMap<KeymapCallback, CallbackFunction, 2>,
195}
196
197impl<
198        I: Debug + Index<usize, Output = R>,
199        R: Debug,
200        Ctx: Debug,
201        Ev: Debug,
202        PKS: Debug,
203        KS: Debug,
204        S: key::System<R, Ref = R, Context = Ctx, Event = Ev, PendingKeyState = PKS, KeyState = KS>,
205    > core::fmt::Debug for Keymap<I, R, Ctx, Ev, PKS, KS, S>
206{
207    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
208        f.debug_struct("Keymap")
209            .field("context", &self.context)
210            .field("event_scheduler", &self.event_scheduler)
211            .field("ms_per_tick", &self.ms_per_tick)
212            .field("idle_time", &self.idle_time)
213            .field("hid_reporter", &self.hid_reporter)
214            .field("input_queue", &self.input_queue)
215            .field("input_queue_delay_counter", &self.input_queue_delay_counter)
216            .field("pending_key_state", &self.pending_key_state)
217            .field("pressed_inputs", &self.pressed_inputs)
218            .finish_non_exhaustive()
219    }
220}
221
222impl<
223        I: Debug + Index<usize, Output = R>,
224        R: Copy + Debug,
225        Ctx: Debug + key::Context<Event = Ev> + SetKeymapContext,
226        Ev: Copy + Debug,
227        PKS: Debug,
228        KS: Copy + Debug,
229        S: key::System<R, Ref = R, Context = Ctx, Event = Ev, PendingKeyState = PKS, KeyState = KS>,
230    > Keymap<I, R, Ctx, Ev, PKS, KS, S>
231where
232    KS: From<key::NoOpKeyState>,
233{
234    /// Constructs a new keymap with the given key definitions and context.
235    pub const fn new(key_refs: I, context: Ctx, key_system: S) -> Self {
236        Self {
237            key_refs,
238            key_system,
239            context,
240            pressed_inputs: heapless::Vec::new(),
241            event_scheduler: EventScheduler::new(),
242            ms_per_tick: 1,
243            idle_time: 0,
244            hid_reporter: HIDKeyboardReporter::new(),
245            pending_key_state: None,
246            input_queue: heapless::spsc::Queue::new(),
247            input_queue_delay_counter: 0,
248            callbacks: heapless::LinearMap::new(),
249        }
250    }
251
252    /// Initializes or resets the keyboard to an initial state.
253    pub fn init(&mut self) {
254        self.pressed_inputs.clear();
255        self.event_scheduler.init();
256        self.hid_reporter.init();
257        self.pending_key_state = None;
258        while !self.input_queue.is_empty() {
259            self.input_queue.dequeue().unwrap();
260        }
261        self.input_queue_delay_counter = 0;
262        self.ms_per_tick = 1;
263        self.idle_time = 0;
264    }
265
266    /// Registers the given callback to the keymap.
267    ///
268    /// Only one callback is set for each callback id.
269    pub fn set_callback(&mut self, callback_id: KeymapCallback, callback_fn: fn() -> ()) {
270        self.callbacks
271            .insert(callback_id, CallbackFunction::Rust(callback_fn))
272            .unwrap();
273    }
274
275    /// Registers the given callback to the keymap.
276    ///
277    /// Only one callback is set for each callback id.
278    pub fn set_callback_extern(
279        &mut self,
280        callback_id: KeymapCallback,
281        callback_fn: extern "C" fn() -> (),
282    ) {
283        self.callbacks
284            .insert(callback_id, CallbackFunction::ExternC(callback_fn))
285            .unwrap();
286    }
287
288    /// Sets the number of ms per tick().
289    pub fn set_ms_per_tick(&mut self, ms_per_tick: u8) {
290        self.ms_per_tick = ms_per_tick;
291    }
292
293    // If the pending key state is resolved,
294    //  then clear the pending key state.
295    fn resolve_pending_key_state(&mut self, key_state: KS) {
296        if let Some(PendingState {
297            keymap_index,
298            key_ref,
299            queued_events,
300            ..
301        }) = self.pending_key_state.take()
302        {
303            // Cancel events which were scheduled for the (pending) key.
304            self.event_scheduler
305                .cancel_events_for_keymap_index(keymap_index);
306
307            // Add the pending state's pressed key to pressed inputs
308            self.pressed_inputs
309                .push(input::PressedInput::pressed_key(
310                    keymap_index,
311                    key_ref,
312                    key_state,
313                ))
314                .unwrap();
315
316            // Schedule each of the queued events,
317            //  delaying each consecutive event by a tick
318            //  (in order to allow press/release events to affect the HID report)
319            let mut i = 1;
320            let mut old_input_queue: heapless::spsc::Queue<input::Event, MAX_QUEUED_INPUT_EVENTS> =
321                core::mem::take(&mut self.input_queue);
322
323            // Partition the events from the pending keymap index
324            //  separately from the other queued events.
325            // (Only queue the *last* event from the pending keymap index).
326            let (pending_input_ev, queued_events): (
327                heapless::Vec<key::Event<Ev>, { MAX_PRESSED_KEYS }>,
328                heapless::Vec<key::Event<Ev>, { MAX_PRESSED_KEYS }>,
329            ) = queued_events.iter().partition(|ev| match ev {
330                key::Event::Input(input::Event::Press {
331                    keymap_index: queued_kmi,
332                }) => *queued_kmi == keymap_index,
333                key::Event::Input(input::Event::Release {
334                    keymap_index: queued_kmi,
335                }) => *queued_kmi == keymap_index,
336                key::Event::Key {
337                    keymap_index: queued_kmi,
338                    ..
339                } => *queued_kmi == keymap_index,
340                _ => false,
341            });
342
343            for ev in queued_events.iter().chain(pending_input_ev.last()) {
344                match ev {
345                    key::Event::Input(ie) => {
346                        self.input_queue.enqueue(*ie).unwrap();
347                    }
348                    _ => {
349                        self.event_scheduler.schedule_after(i, *ev);
350                        i += 1;
351                    }
352                }
353            }
354
355            while let Some(ie) = old_input_queue.dequeue() {
356                self.input_queue.enqueue(ie).unwrap();
357            }
358
359            self.handle_pending_events();
360
361            // The resolved key state has output. Emit this as an event.
362            if let Some(key_output) = self.key_system.key_output(&key_ref, &key_state) {
363                let km_ev = KeymapEvent::ResolvedKeyOutput {
364                    keymap_index,
365                    key_output,
366                };
367                self.handle_event(key::Event::Keymap(km_ev));
368            }
369        }
370    }
371
372    /// Handles input events.
373    pub fn handle_input(&mut self, ev: input::Event) {
374        self.idle_time = 0;
375
376        if self.input_queue.is_full() {
377            return;
378        }
379
380        self.input_queue.enqueue(ev).unwrap();
381
382        if self.input_queue_delay_counter == 0 {
383            let ie = self.input_queue.dequeue().unwrap();
384            self.process_input(ie);
385            self.input_queue_delay_counter = INPUT_QUEUE_TICK_DELAY;
386        }
387    }
388
389    fn has_pressed_input_with_keymap_index(&self, keymap_index: u16) -> bool {
390        self.pressed_inputs.iter().any(|pi| match pi {
391            &input::PressedInput::Key(input::PressedKey {
392                keymap_index: ki, ..
393            }) => keymap_index == ki,
394            _ => false,
395        })
396    }
397
398    fn update_pending_state(&mut self, ev: key::Event<Ev>) {
399        if let Some(PendingState {
400            keymap_index,
401            key_ref,
402            pending_key_state,
403            queued_events,
404            ..
405        }) = &mut self.pending_key_state
406        {
407            let (mut maybe_npk, pke) = self.key_system.update_pending_state(
408                pending_key_state,
409                *keymap_index,
410                &self.context,
411                *key_ref,
412                ev,
413            );
414
415            pke.into_iter()
416                .for_each(|sch_ev| self.event_scheduler.schedule_event(sch_ev));
417
418            while let Some(npk) = maybe_npk.take() {
419                let pkr = match npk {
420                    key::NewPressedKey::Key(new_key_ref) => {
421                        *key_ref = new_key_ref;
422                        let (pkr, pke) = self.key_system.new_pressed_key(
423                            *keymap_index,
424                            &self.context,
425                            new_key_ref,
426                        );
427                        pke.into_iter()
428                            .for_each(|sch_ev| self.event_scheduler.schedule_event(sch_ev));
429                        pkr
430                    }
431                    key::NewPressedKey::NoOp => {
432                        let no_op_ks: KS = key::NoOpKeyState.into();
433                        key::PressedKeyResult::Resolved(no_op_ks)
434                    }
435                };
436
437                match pkr {
438                    key::PressedKeyResult::Resolved(ks) => {
439                        self.resolve_pending_key_state(ks);
440                        break;
441                    }
442                    key::PressedKeyResult::NewPressedKey(key::NewPressedKey::Key(new_key_ref)) => {
443                        maybe_npk = Some(key::NewPressedKey::Key(new_key_ref));
444                    }
445                    key::PressedKeyResult::NewPressedKey(key::NewPressedKey::NoOp) => {
446                        self.resolve_pending_key_state(key::NoOpKeyState.into());
447                        break;
448                    }
449                    key::PressedKeyResult::Pending(pks) => {
450                        *pending_key_state = pks;
451
452                        // Since the pending key state resolved into another pending key state,
453                        //  we re-queue all the input events that had been received.
454                        let orig_input_queue = core::mem::take(&mut self.input_queue);
455                        while let Some(ev) = queued_events.pop() {
456                            if let key::Event::Input(input_ev) = ev {
457                                self.input_queue.enqueue(input_ev).unwrap();
458                            }
459                        }
460                        orig_input_queue.iter().for_each(|&ev| {
461                            self.input_queue.enqueue(ev).unwrap();
462                        });
463                    }
464                }
465            }
466        }
467    }
468
469    fn process_input(&mut self, ev: input::Event) {
470        if let Some(pending_state) = &mut self.pending_key_state {
471            pending_state.queued_events.push(ev.into()).unwrap();
472            self.update_pending_state(ev.into());
473        } else {
474            // Update each of the pressed keys with the event.
475            self.pressed_inputs.iter_mut().for_each(|pi| {
476                if let input::PressedInput::Key(input::PressedKey {
477                    key_ref,
478                    key_state,
479                    keymap_index,
480                }) = pi
481                {
482                    self.key_system
483                        .update_state(key_state, key_ref, &self.context, *keymap_index, ev.into())
484                        .into_iter()
485                        .for_each(|sch_ev| self.event_scheduler.schedule_event(sch_ev));
486                }
487            });
488
489            self.context
490                .handle_event(ev.into())
491                .into_iter()
492                .for_each(|sch_ev| self.event_scheduler.schedule_event(sch_ev));
493
494            match ev {
495                input::Event::Press { keymap_index }
496                    if !self.has_pressed_input_with_keymap_index(keymap_index) =>
497                {
498                    let mut maybe_key_ref = Some(self.key_refs[keymap_index as usize]);
499
500                    while let Some(key_ref) = maybe_key_ref.take() {
501                        let (pkr, pke) =
502                            self.key_system
503                                .new_pressed_key(keymap_index, &self.context, key_ref);
504
505                        pke.into_iter()
506                            .for_each(|sch_ev| self.event_scheduler.schedule_event(sch_ev));
507
508                        match pkr {
509                            key::PressedKeyResult::Resolved(key_state) => {
510                                self.pressed_inputs
511                                    .push(input::PressedInput::pressed_key(
512                                        keymap_index,
513                                        key_ref,
514                                        key_state,
515                                    ))
516                                    .unwrap();
517
518                                // The resolved key state has output. Emit this as an event.
519                                if let Some(key_output) =
520                                    self.key_system.key_output(&key_ref, &key_state)
521                                {
522                                    let km_ev = KeymapEvent::ResolvedKeyOutput {
523                                        keymap_index,
524                                        key_output,
525                                    };
526                                    self.handle_event(key::Event::Keymap(km_ev));
527                                }
528                            }
529                            key::PressedKeyResult::NewPressedKey(key::NewPressedKey::Key(
530                                new_key_ref,
531                            )) => {
532                                maybe_key_ref = Some(new_key_ref);
533                            }
534                            key::PressedKeyResult::NewPressedKey(key::NewPressedKey::NoOp) => {
535                                let key_state: KS = key::NoOpKeyState.into();
536
537                                self.pressed_inputs
538                                    .push(input::PressedInput::pressed_key(
539                                        keymap_index,
540                                        key_ref,
541                                        key_state,
542                                    ))
543                                    .unwrap();
544                            }
545                            key::PressedKeyResult::Pending(pending_key_state) => {
546                                self.pending_key_state = Some(PendingState {
547                                    keymap_index,
548                                    key_ref,
549                                    pending_key_state,
550                                    queued_events: heapless::Vec::new(),
551                                });
552                            }
553                        }
554                    }
555                }
556                input::Event::Release { keymap_index } => {
557                    self.pressed_inputs
558                        .iter()
559                        .position(|pi| match pi {
560                            &input::PressedInput::Key(input::PressedKey {
561                                keymap_index: ki,
562                                ..
563                            }) => keymap_index == ki,
564                            _ => false,
565                        })
566                        .map(|i| self.pressed_inputs.remove(i));
567
568                    self.event_scheduler
569                        .cancel_events_for_keymap_index(keymap_index);
570                }
571
572                input::Event::VirtualKeyPress { key_output } => {
573                    let pressed_key = input::PressedInput::Virtual(key_output);
574                    self.pressed_inputs.push(pressed_key).unwrap();
575                }
576                input::Event::VirtualKeyRelease { key_output } => {
577                    // Remove from pressed keys.
578                    self.pressed_inputs
579                        .iter()
580                        .position(|k| match k {
581                            input::PressedInput::Virtual(ko) => key_output == *ko,
582                            _ => false,
583                        })
584                        .map(|i| self.pressed_inputs.remove(i));
585                }
586
587                _ => {}
588            }
589        }
590
591        self.handle_pending_events();
592    }
593
594    // Called from handle_all_pending_events,
595    //  and for handling the (resolving) queue of events from pending key state.
596    fn handle_event(&mut self, ev: key::Event<Ev>) {
597        if let key::Event::Keymap(KeymapEvent::Callback(callback_id)) = ev {
598            match self.callbacks.get(&callback_id) {
599                Some(CallbackFunction::Rust(callback_fn)) => {
600                    callback_fn();
601                }
602                Some(CallbackFunction::ExternC(callback_fn)) => {
603                    callback_fn();
604                }
605                None => {}
606            }
607        }
608
609        // pending state needs to handle events
610        self.update_pending_state(ev);
611
612        // Update each of the pressed keys with the event.
613        self.pressed_inputs.iter_mut().for_each(|pi| {
614            if let input::PressedInput::Key(input::PressedKey {
615                key_state,
616                key_ref,
617                keymap_index,
618            }) = pi
619            {
620                self.key_system
621                    .update_state(key_state, key_ref, &self.context, *keymap_index, ev)
622                    .into_iter()
623                    .for_each(|sch_ev| self.event_scheduler.schedule_event(sch_ev));
624            }
625        });
626
627        // Update context with the event
628        self.context
629            .handle_event(ev)
630            .into_iter()
631            .for_each(|sch_ev| self.event_scheduler.schedule_event(sch_ev));
632
633        if let Event::Input(input_ev) = ev {
634            self.process_input(input_ev);
635        }
636    }
637
638    fn handle_pending_events(&mut self) {
639        // take from pending
640        while let Some(ev) = self.event_scheduler.dequeue() {
641            self.handle_event(ev);
642        }
643    }
644
645    /// Advances the state of the keymap by one tick.
646    pub fn tick(&mut self) {
647        let km_context = KeymapContext {
648            time_ms: self.event_scheduler.schedule_counter,
649            idle_time_ms: self.idle_time,
650        };
651        self.context.set_keymap_context(km_context);
652
653        if !self.input_queue.is_empty() && self.input_queue_delay_counter == 0 {
654            let ie = self.input_queue.dequeue().unwrap();
655            self.process_input(ie);
656            self.input_queue_delay_counter = INPUT_QUEUE_TICK_DELAY;
657        }
658
659        if self.input_queue_delay_counter > 0 {
660            self.input_queue_delay_counter -= 1;
661        }
662
663        self.event_scheduler.tick(self.ms_per_tick);
664
665        self.handle_pending_events();
666
667        self.idle_time += self.ms_per_tick as u32;
668    }
669
670    /// Returns the the pressed key outputs.
671    pub fn pressed_keys(&self) -> heapless::Vec<key::KeyOutput, { MAX_PRESSED_KEYS }> {
672        let pressed_key_codes = self.pressed_inputs.iter().filter_map(|pi| match pi {
673            input::PressedInput::Key(input::PressedKey {
674                key_ref, key_state, ..
675            }) => self.key_system.key_output(key_ref, key_state),
676            &input::PressedInput::Virtual(key_output) => Some(key_output),
677        });
678
679        pressed_key_codes.collect()
680    }
681
682    /// Updates the keymap indicating a report is sent; returns the reportable keymap output.
683    pub fn report_output(&mut self) -> KeymapOutput {
684        self.hid_reporter.update(self.pressed_keys());
685        self.hid_reporter.report_sent();
686
687        KeymapOutput::new(self.hid_reporter.reportable_key_outputs())
688    }
689
690    /// Returns the current HID keyboard report.
691    #[doc(hidden)]
692    pub fn boot_keyboard_report(&self) -> [u8; 8] {
693        KeymapOutput::new(self.pressed_keys()).as_hid_boot_keyboard_report()
694    }
695
696    #[doc(hidden)]
697    pub fn has_scheduled_events(&self) -> bool {
698        !self.event_scheduler.pending_events.is_empty()
699            || !self.event_scheduler.scheduled_events.is_empty()
700            || !self.input_queue.is_empty()
701    }
702}
703
704#[cfg(test)]
705mod tests {
706    use super::*;
707
708    #[test]
709    fn test_keymap_output_pressed_key_codes_includes_modifier_key_code() {
710        // Assemble - include modifier key left ctrl
711        let mut input: heapless::Vec<key::KeyOutput, { MAX_PRESSED_KEYS }> = heapless::Vec::new();
712        input.push(key::KeyOutput::from_key_code(0x04)).unwrap();
713        input.push(key::KeyOutput::from_key_code(0xE0)).unwrap();
714
715        // Act - construct the output
716        let keymap_output = KeymapOutput::new(input);
717        let pressed_key_codes = keymap_output.pressed_key_codes();
718
719        // Assert - check the 0xE0 gets included as a key code.
720        assert!(pressed_key_codes.contains(&0xE0))
721    }
722
723    #[test]
724    fn test_keymap_output_as_hid_boot_keyboard_report_gathers_modifiers() {
725        // Assemble - include modifier key left ctrl
726        let mut input: heapless::Vec<key::KeyOutput, { MAX_PRESSED_KEYS }> = heapless::Vec::new();
727        input.push(key::KeyOutput::from_key_code(0x04)).unwrap();
728        input.push(key::KeyOutput::from_key_code(0xE0)).unwrap();
729
730        // Act - construct the output
731        let keymap_output = KeymapOutput::new(input);
732        let actual_report: [u8; 8] = keymap_output.as_hid_boot_keyboard_report();
733
734        // Assert - check the 0xE0 gets considered as a "modifier".
735        let expected_report: [u8; 8] = [0x01, 0, 0x04, 0, 0, 0, 0, 0];
736        assert_eq!(expected_report, actual_report);
737    }
738
739    #[test]
740    fn test_keymap_many_input_events_without_tick_or_report() {
741        // Assemble
742        let mut keymap = {
743            use crate as smart_keymap;
744            use smart_keymap::key::composite as key_system;
745
746            use key_system::Context;
747            use key_system::Ref;
748            const KEY_COUNT: usize = 1;
749            const KEY_REFS: [Ref; KEY_COUNT] = [smart_keymap::key::composite::Ref::Keyboard(
750                smart_keymap::key::keyboard::Ref::KeyCode(0x04),
751            )];
752            const CONTEXT: Context = Context::from_config(key_system::DEFAULT_CONFIG);
753
754            smart_keymap::keymap::Keymap::new(
755                KEY_REFS,
756                CONTEXT,
757                smart_keymap::key::composite::System::array_based(
758                    smart_keymap::key::keyboard::System::new([]),
759                    smart_keymap::key::callback::System::new([]),
760                    smart_keymap::key::sticky::System::new([]),
761                    smart_keymap::key::tap_dance::System::new([]),
762                    smart_keymap::key::tap_hold::System::new([]),
763                    smart_keymap::key::layered::System::new([], []),
764                    smart_keymap::key::chorded::System::new([], []),
765                ),
766            )
767        };
768
769        // Act
770        for _ in 0..100 {
771            keymap.handle_input(input::Event::Press { keymap_index: 0 });
772            keymap.handle_input(input::Event::Release { keymap_index: 0 });
773        }
774
775        // Assert
776        // (expect no panics)
777    }
778}