Skip to main content

smart_keymap_core/
keymap.rs

1#[cfg(feature = "std")]
2mod distinct_reports;
3mod event_scheduler;
4/// The HID keyboard reporter.
5pub mod hid_keyboard_reporter;
6mod input_event_queue;
7#[cfg(feature = "std")]
8mod observed_eb_keymap;
9#[cfg(feature = "std")]
10mod observed_keymap;
11mod pending;
12
13use core::cmp::PartialEq;
14use core::fmt::Debug;
15use core::marker::Copy;
16use core::ops::Index;
17
18use serde::Deserialize;
19
20use crate::input;
21use crate::key;
22
23use key::Event;
24
25#[cfg(feature = "std")]
26pub use distinct_reports::DistinctReports;
27use event_scheduler::EventScheduler;
28use hid_keyboard_reporter::HIDKeyboardReporter;
29use input_event_queue::InputEventQueue;
30#[cfg(feature = "std")]
31pub use observed_eb_keymap::ObservedKeymap as ObservedEventBasedKeymap;
32#[cfg(feature = "std")]
33pub use observed_keymap::ObservedKeymap;
34
35/// Maximum number of pressed keys supported.
36pub const MAX_PRESSED_KEYS: usize = 16;
37
38pub(crate) const MAX_QUEUED_INPUT_EVENTS: usize = 32;
39
40/// Constructs an HID report or a sequence of key codes from the given sequence of [key::KeyOutput].
41#[derive(Debug, Default, PartialEq)]
42pub struct KeymapOutput {
43    pressed_key_codes: heapless::Vec<key::KeyOutput, { MAX_PRESSED_KEYS }>,
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 consumer codes.
107    pub fn pressed_consumer_codes(&self) -> heapless::Vec<u8, 24> {
108        self.pressed_key_codes
109            .iter()
110            .flat_map(|ko| match ko.key_code() {
111                key::KeyUsage::Consumer(uc) => Some(uc),
112                _ => None,
113            })
114            .collect()
115    }
116
117    /// Returns the pressed custom codes.
118    pub fn pressed_custom_codes(&self) -> heapless::Vec<u8, 24> {
119        self.pressed_key_codes
120            .iter()
121            .flat_map(|ko| match ko.key_code() {
122                key::KeyUsage::Custom(kc) => Some(kc),
123                _ => None,
124            })
125            .collect()
126    }
127
128    /// Returns the combined pressed mouse output.
129    pub fn pressed_mouse_output(&self) -> key::MouseOutput {
130        self.pressed_key_codes
131            .iter()
132            .filter_map(|ko| match ko.key_code() {
133                key::KeyUsage::Mouse(mo) => Some(mo),
134                _ => None,
135            })
136            .fold(key::MouseOutput::NO_OUTPUT, |acc, mo| acc.combine(&mo))
137    }
138}
139
140/// Commands for managing Bluetooth profiles. (BLE pairing and bonding).
141#[derive(Deserialize, Debug, Clone, Copy, Eq, PartialEq)]
142pub enum BluetoothProfileCommand {
143    /// Disconnect the current profile.
144    Disconnect,
145    /// Clear the current profile. (Start pairing mode).
146    Clear,
147    /// Clear all profiles. (Start pairing mode).
148    ClearAll,
149    /// Switch to the previous profile.
150    Previous,
151    /// Switch to the next profile.
152    Next,
153    /// Switch to the given profile index.
154    Select(u8),
155}
156
157/// Callbacks for effect keys in the keymap.
158#[derive(Deserialize, Debug, Clone, Copy, Eq, PartialEq)]
159pub enum KeymapCallback {
160    /// Reset the keyboard
161    Reset,
162    /// Reset the keyboard to bootloader
163    ResetToBootloader,
164    /// Reset the keyboard to bootloader
165    Bluetooth(BluetoothProfileCommand),
166    /// A custom callback. Its behaviour is specific to the firmware implementation.
167    Custom(u8, u8),
168}
169
170/// Context provided from the keymap to the smart keys.
171#[derive(Debug, Clone, Copy, Default)]
172pub struct KeymapContext {
173    /// Number of milliseconds since keymap has been initialized.
174    pub time_ms: u32,
175
176    /// Number of milliseconds since keymap received an input event.
177    pub idle_time_ms: u32,
178}
179
180impl KeymapContext {
181    /// Constructs a new default keymap context.
182    pub const fn new() -> Self {
183        KeymapContext {
184            time_ms: 0,
185            idle_time_ms: 0,
186        }
187    }
188}
189
190/// Trait for setting the keymap context.
191pub trait SetKeymapContext {
192    /// Sets the keymap context.
193    fn set_keymap_context(&mut self, context: KeymapContext);
194}
195
196/// Events related to the keymap.
197#[derive(Debug, Clone, Copy, PartialEq, Eq)]
198pub enum KeymapEvent {
199    /// Callback event (emitted by callback key).
200    Callback(KeymapCallback),
201    /// A pressed key resolved to a state with this key output.
202    ResolvedKeyOutput {
203        /// The keymap index of the key which resolved to the output.
204        keymap_index: u16,
205        /// The resolved key output.
206        key_output: key::KeyOutput,
207    },
208}
209
210#[derive(Debug)]
211enum CallbackFunction {
212    /// C callback
213    ExternC(extern "C" fn() -> ()),
214    /// Rust callback
215    Rust(fn() -> ()),
216}
217
218/// State for a keymap that handles input, and outputs HID keyboard reports.
219pub struct Keymap<I: Index<usize, Output = R>, R, Ctx, Ev: Debug, PKS, KS, S> {
220    key_refs: I,
221    key_system: S,
222    context: Ctx,
223    pressed_inputs: heapless::Vec<input::PressedInput<R, KS>, { MAX_PRESSED_KEYS }>,
224    event_scheduler: EventScheduler<Ev>,
225    ms_per_tick: u8,
226    idle_time: u32,
227    hid_reporter: HIDKeyboardReporter,
228    pending_state: Option<pending::PendingState<R, Ev, PKS>>,
229    input_queue: InputEventQueue<{ MAX_QUEUED_INPUT_EVENTS }>,
230    callbacks: heapless::LinearMap<KeymapCallback, CallbackFunction, 2>,
231}
232
233impl<
234        I: Debug + Index<usize, Output = R>,
235        R: Debug,
236        Ctx: Debug,
237        Ev: Debug,
238        PKS: Debug,
239        KS: Debug,
240        S: Debug,
241    > core::fmt::Debug for Keymap<I, R, Ctx, Ev, PKS, KS, S>
242{
243    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
244        f.debug_struct("Keymap")
245            .field("context", &self.context)
246            .field("event_scheduler", &self.event_scheduler)
247            .field("ms_per_tick", &self.ms_per_tick)
248            .field("idle_time", &self.idle_time)
249            .field("hid_reporter", &self.hid_reporter)
250            .field("input_queue", &self.input_queue)
251            .field("pending_state", &self.pending_state)
252            .field("pressed_inputs", &self.pressed_inputs)
253            .finish_non_exhaustive()
254    }
255}
256
257impl<
258        I: Debug + Index<usize, Output = R>,
259        R: Copy + Debug,
260        Ctx: Debug + key::Context<Event = Ev> + SetKeymapContext,
261        Ev: Copy + Debug,
262        PKS: Debug,
263        KS: Copy + Debug + From<key::NoOpKeyState>,
264        S: key::System<R, Ref = R, Context = Ctx, Event = Ev, PendingKeyState = PKS, KeyState = KS>,
265    > Keymap<I, R, Ctx, Ev, PKS, KS, S>
266{
267    /// Constructs a new keymap with the given key definitions and context.
268    pub const fn new(key_refs: I, context: Ctx, key_system: S) -> Self {
269        Self {
270            key_refs,
271            key_system,
272            context,
273            pressed_inputs: heapless::Vec::new(),
274            event_scheduler: EventScheduler::new(),
275            ms_per_tick: 1,
276            idle_time: 0,
277            hid_reporter: HIDKeyboardReporter::new(),
278            pending_state: None,
279            input_queue: InputEventQueue::new(),
280            callbacks: heapless::LinearMap::new(),
281        }
282    }
283
284    /// Initializes or resets the keyboard to an initial state.
285    ///
286    /// Resets [key::Context] from each family's config (clearing active layers and
287    /// other runtime state while keeping that keymap's config), and clears pressed
288    /// keys, pending work, and HID report state.
289    pub fn init(&mut self) {
290        self.context.reset();
291        self.pressed_inputs.clear();
292        self.event_scheduler.init();
293        self.hid_reporter.init();
294        self.pending_state = None;
295        self.input_queue.clear();
296        self.ms_per_tick = 1;
297        self.idle_time = 0;
298    }
299
300    /// Clears all registered callbacks.
301    pub fn clear_callbacks(&mut self) {
302        self.callbacks.clear();
303    }
304
305    /// Registers the given callback to the keymap.
306    ///
307    /// Only one callback is set for each callback id.
308    pub fn set_callback(&mut self, callback_id: KeymapCallback, callback_fn: fn() -> ()) {
309        let _ = self
310            .callbacks
311            .insert(callback_id, CallbackFunction::Rust(callback_fn));
312    }
313
314    /// Registers the given callback to the keymap.
315    ///
316    /// Only one callback is set for each callback id.
317    pub fn set_callback_extern(
318        &mut self,
319        callback_id: KeymapCallback,
320        callback_fn: extern "C" fn() -> (),
321    ) {
322        let _ = self
323            .callbacks
324            .insert(callback_id, CallbackFunction::ExternC(callback_fn));
325    }
326
327    /// Sets the number of ms per tick().
328    pub fn set_ms_per_tick(&mut self, ms_per_tick: u8) {
329        self.ms_per_tick = ms_per_tick;
330    }
331
332    // If the pending key state is resolved,
333    //  then clear the pending key state.
334    //
335    // Replay uses only `queued_events` (the session log).
336    // Inputs still waiting in the pending `ingest_queue` (the delay line)
337    //  are intentionally omitted from replay -
338    //  they were not yet paced/applied during pending -
339    //  and are transferred to the global `input_queue` tail
340    //  to run post-resolve in normal order.
341    fn resolve_pending_key_state(&mut self, key_state: KS) {
342        if let Some(pending::PendingState {
343            keymap_index,
344            key_ref,
345            mut queued_events,
346            mut ingest_queue,
347            ..
348        }) = self.pending_state.take()
349        {
350            // Cancel events which were scheduled for the (pending) key.
351            self.event_scheduler
352                .cancel_events_for_keymap_index(keymap_index);
353
354            // Add the pending state's pressed key to pressed inputs
355            let _ = self.pressed_inputs.push(input::PressedInput::pressed_key(
356                keymap_index,
357                key_ref,
358                key_state,
359            ));
360
361            // Session-log replay is prepended onto the global queue so it
362            //  runs before any never-logged delay-line inputs transferred next.
363            pending::dispatch_replayed_events(
364                pending::KeyResolution::Resolved { keymap_index },
365                &mut queued_events,
366                &mut self.input_queue,
367                &mut self.event_scheduler,
368            );
369
370            // Transfer remaining pending delay-line traffic to the global tail.
371            let mut remaining = ingest_queue.take_all();
372            self.input_queue.append_all(&mut remaining);
373
374            self.handle_pending_events();
375
376            // The resolved key state has output. Emit this as an event.
377            if let Some(key_output) = self.key_system.key_output(&key_ref, &key_state) {
378                let km_ev = KeymapEvent::ResolvedKeyOutput {
379                    keymap_index,
380                    key_output,
381                };
382                self.handle_event(key::Event::Keymap(km_ev));
383            }
384        }
385    }
386
387    /// Handles input events.
388    ///
389    /// Physical inputs enter a delay line first so at most one is processed
390    ///  per tick (one-tick pacing gate), including while a key is pending.
391    /// While pending, that delay line is the pending session's `ingest_queue`;
392    ///  otherwise it is the global `input_queue`.
393    /// Tap-hold and chorded interrupt logic depend on that spacing
394    ///  (`tests/rust/tap_hold/hold_on_interrupt_tap.rs`).
395    ///
396    /// Silently discards the input event if the active input queue is full.
397    pub fn handle_input(&mut self, ev: input::Event) {
398        self.idle_time = 0;
399
400        let ready = if let Some(pending_state) = self.pending_state.as_mut() {
401            pending_state.ingest_queue.push_back_or_ignore(ev);
402            pending_state.ingest_queue.pop_front_if_ready()
403        } else {
404            self.input_queue.push_back_or_ignore(ev);
405            self.input_queue.pop_front_if_ready()
406        };
407
408        if let Some(ie) = ready {
409            self.process_input(ie);
410            self.set_active_input_delay();
411        }
412    }
413
414    /// After processing one input, re-arm the active delay line.
415    ///
416    /// If processing resolved pending state, the global queue is active;
417    ///  if a pending session remains (or was just created), its ingest queue is.
418    fn set_active_input_delay(&mut self) {
419        if let Some(pending_state) = self.pending_state.as_mut() {
420            pending_state.ingest_queue.set_delay();
421        } else {
422            self.input_queue.set_delay();
423        }
424    }
425
426    fn has_pressed_input_with_keymap_index(&self, keymap_index: u16) -> bool {
427        self.pressed_inputs.iter().any(|pi| match pi {
428            &input::PressedInput::Key(input::PressedKey {
429                keymap_index: ki, ..
430            }) => keymap_index == ki,
431            _ => false,
432        })
433    }
434
435    fn update_pending_state(&mut self, ev: key::Event<Ev>) {
436        if let Some(pending::PendingState {
437            keymap_index,
438            key_ref,
439            pending_key_state,
440            queued_events,
441            ingest_queue,
442            ..
443        }) = self.pending_state.as_mut()
444        {
445            let (mut maybe_npk, pke) = self.key_system.update_pending_state(
446                pending_key_state,
447                *keymap_index,
448                &self.context,
449                *key_ref,
450                ev,
451            );
452
453            pke.into_iter()
454                .for_each(|sch_ev| self.event_scheduler.schedule_event(sch_ev));
455
456            while let Some(npk) = maybe_npk.take() {
457                let pkr = match npk {
458                    key::NewPressedKey::Key(new_key_ref) => {
459                        *key_ref = new_key_ref;
460                        let (pkr, pke) = self.key_system.new_pressed_key(
461                            *keymap_index,
462                            &self.context,
463                            new_key_ref,
464                        );
465                        pke.into_iter()
466                            .for_each(|sch_ev| self.event_scheduler.schedule_event(sch_ev));
467                        pkr
468                    }
469                    key::NewPressedKey::NoOp => {
470                        let no_op_ks: KS = key::NoOpKeyState.into();
471                        key::PressedKeyResult::Resolved(no_op_ks)
472                    }
473                };
474
475                match pkr {
476                    key::PressedKeyResult::Resolved(ks) => {
477                        self.resolve_pending_key_state(ks);
478                        break;
479                    }
480                    key::PressedKeyResult::NewPressedKey(key::NewPressedKey::Key(new_key_ref)) => {
481                        maybe_npk = Some(key::NewPressedKey::Key(new_key_ref));
482                    }
483                    key::PressedKeyResult::NewPressedKey(key::NewPressedKey::NoOp) => {
484                        self.resolve_pending_key_state(key::NoOpKeyState.into());
485                        break;
486                    }
487                    key::PressedKeyResult::Pending(pks) => {
488                        *pending_key_state = pks;
489
490                        // Nested pending: re-feed session-log inputs chronologically
491                        //  into the current pending delay line (not the global queue).
492                        pending::dispatch_replayed_events(
493                            pending::KeyResolution::Pending,
494                            queued_events,
495                            ingest_queue,
496                            &mut self.event_scheduler,
497                        );
498                    }
499                }
500            }
501        }
502    }
503
504    fn process_input(&mut self, ev: input::Event) {
505        if let Some(pending_state) = self.pending_state.as_mut() {
506            // Paced input from the delay line: record in the session log, then apply.
507            pending_state.record_input(ev);
508            self.update_pending_state(ev.into());
509        } else {
510            // Update each of the pressed keys with the event.
511            self.pressed_inputs.iter_mut().for_each(|pi| {
512                if let input::PressedInput::Key(input::PressedKey {
513                    key_ref,
514                    key_state,
515                    keymap_index,
516                }) = pi
517                {
518                    self.key_system
519                        .update_state(key_state, key_ref, &self.context, *keymap_index, ev.into())
520                        .into_iter()
521                        .for_each(|sch_ev| self.event_scheduler.schedule_event(sch_ev));
522                }
523            });
524
525            self.context
526                .handle_event(ev.into())
527                .into_iter()
528                .for_each(|sch_ev| self.event_scheduler.schedule_event(sch_ev));
529
530            match ev {
531                input::Event::Press { keymap_index }
532                    if !self.has_pressed_input_with_keymap_index(keymap_index) =>
533                {
534                    let mut maybe_key_ref = Some(self.key_refs[keymap_index as usize]);
535
536                    while let Some(key_ref) = maybe_key_ref.take() {
537                        let (pkr, pke) =
538                            self.key_system
539                                .new_pressed_key(keymap_index, &self.context, key_ref);
540
541                        pke.into_iter()
542                            .for_each(|sch_ev| self.event_scheduler.schedule_event(sch_ev));
543
544                        match pkr {
545                            key::PressedKeyResult::Resolved(key_state) => {
546                                let _ = self.pressed_inputs.push(input::PressedInput::pressed_key(
547                                    keymap_index,
548                                    key_ref,
549                                    key_state,
550                                ));
551
552                                // The resolved key state has output. Emit this as an event.
553                                if let Some(key_output) =
554                                    self.key_system.key_output(&key_ref, &key_state)
555                                {
556                                    let km_ev = KeymapEvent::ResolvedKeyOutput {
557                                        keymap_index,
558                                        key_output,
559                                    };
560                                    self.handle_event(key::Event::Keymap(km_ev));
561                                }
562                            }
563                            key::PressedKeyResult::NewPressedKey(key::NewPressedKey::Key(
564                                new_key_ref,
565                            )) => {
566                                maybe_key_ref = Some(new_key_ref);
567                            }
568                            key::PressedKeyResult::NewPressedKey(key::NewPressedKey::NoOp) => {
569                                let key_state: KS = key::NoOpKeyState.into();
570
571                                let _ = self.pressed_inputs.push(input::PressedInput::pressed_key(
572                                    keymap_index,
573                                    key_ref,
574                                    key_state,
575                                ));
576                            }
577                            key::PressedKeyResult::Pending(pending_key_state) => {
578                                // Fresh pending session owns its own delay line,
579                                //  armed so the next physical input is deferred.
580                                // Move any inputs already sitting in the global
581                                //  queue (e.g. a rapid release pushed before this
582                                //  press was processed) into the new local delay
583                                //  line so they are paced while pending.
584                                let mut pending_state = pending::PendingState::new(
585                                    keymap_index,
586                                    key_ref,
587                                    pending_key_state,
588                                );
589                                let mut remaining = self.input_queue.take_all();
590                                pending_state.ingest_queue.append_all(&mut remaining);
591                                self.pending_state = Some(pending_state);
592                            }
593                        }
594                    }
595                }
596                input::Event::Release { keymap_index } => {
597                    self.pressed_inputs
598                        .iter()
599                        .position(|pi| match pi {
600                            &input::PressedInput::Key(input::PressedKey {
601                                keymap_index: ki,
602                                ..
603                            }) => keymap_index == ki,
604                            _ => false,
605                        })
606                        .map(|i| self.pressed_inputs.remove(i));
607                }
608
609                input::Event::VirtualKeyPress { key_output } => {
610                    let pressed_key = input::PressedInput::Virtual(key_output);
611                    let _ = self.pressed_inputs.push(pressed_key);
612                }
613                input::Event::VirtualKeyRelease { key_output } => {
614                    // Remove from pressed keys.
615                    self.pressed_inputs
616                        .iter()
617                        .position(|k| match k {
618                            input::PressedInput::Virtual(ko) => key_output == *ko,
619                            _ => false,
620                        })
621                        .map(|i| self.pressed_inputs.remove(i));
622                }
623
624                _ => {}
625            }
626        }
627
628        self.handle_pending_events();
629    }
630
631    // Called from handle_all_pending_events,
632    //  and for handling the (resolving) queue of events from pending key state.
633    fn handle_event(&mut self, ev: key::Event<Ev>) {
634        if let key::Event::Keymap(KeymapEvent::Callback(callback_id)) = ev {
635            match self.callbacks.get(&callback_id) {
636                Some(CallbackFunction::Rust(callback_fn)) => {
637                    callback_fn();
638                }
639                Some(CallbackFunction::ExternC(callback_fn)) => {
640                    callback_fn();
641                }
642                None => {}
643            }
644        }
645
646        let was_pending = self.pending_state.is_some();
647
648        // pending state needs to handle events
649        self.update_pending_state(ev);
650
651        // Update each of the pressed keys with the event.
652        self.pressed_inputs.iter_mut().for_each(|pi| {
653            if let input::PressedInput::Key(input::PressedKey {
654                key_state,
655                key_ref,
656                keymap_index,
657            }) = pi
658            {
659                self.key_system
660                    .update_state(key_state, key_ref, &self.context, *keymap_index, ev)
661                    .into_iter()
662                    .for_each(|sch_ev| self.event_scheduler.schedule_event(sch_ev));
663            }
664        });
665
666        // Update context with the event
667        self.context
668            .handle_event(ev)
669            .into_iter()
670            .for_each(|sch_ev| self.event_scheduler.schedule_event(sch_ev));
671
672        if let Event::Input(input_ev) = ev {
673            if was_pending {
674                // `update_pending_state` already ran above.
675                // Only record for replay if still pending;
676                //  do not re-apply or fall through to the non-pending press path.
677                if let Some(pending_state) = self.pending_state.as_mut() {
678                    pending_state.record_input(input_ev);
679                }
680                self.handle_pending_events();
681            } else {
682                self.process_input(input_ev);
683            }
684        }
685    }
686
687    fn handle_pending_events(&mut self) {
688        // take from pending
689        while let Some(ev) = self.event_scheduler.dequeue() {
690            self.handle_event(ev);
691        }
692    }
693
694    /// Advances the state of the keymap by one tick.
695    pub fn tick(&mut self) {
696        let km_context = KeymapContext {
697            time_ms: self.event_scheduler.schedule_counter,
698            idle_time_ms: self.idle_time,
699        };
700        self.context.set_keymap_context(km_context);
701
702        let ready = if let Some(pending_state) = self.pending_state.as_mut() {
703            pending_state.ingest_queue.pop_front_if_ready()
704        } else {
705            self.input_queue.pop_front_if_ready()
706        };
707
708        if let Some(ie) = ready {
709            self.process_input(ie);
710            self.set_active_input_delay();
711        }
712
713        // Always tick the global delay gate so it does not go stale
714        //  across a pending session (e.g. on resolve transfer).
715        self.input_queue.tick_delay();
716        if let Some(pending_state) = self.pending_state.as_mut() {
717            pending_state.ingest_queue.tick_delay();
718        }
719
720        self.event_scheduler.tick(self.ms_per_tick);
721
722        self.handle_pending_events();
723
724        self.idle_time += self.ms_per_tick as u32;
725    }
726
727    /// Returns the the pressed key outputs.
728    pub fn pressed_keys(&self) -> heapless::Vec<key::KeyOutput, { MAX_PRESSED_KEYS }> {
729        let pressed_key_codes = self.pressed_inputs.iter().filter_map(|pi| match pi {
730            input::PressedInput::Key(input::PressedKey {
731                key_ref, key_state, ..
732            }) => self.key_system.key_output(key_ref, key_state),
733            &input::PressedInput::Virtual(key_output) => Some(key_output),
734        });
735
736        pressed_key_codes.collect()
737    }
738
739    fn tick_by(&mut self, delta_ms: u32) {
740        if delta_ms == 0 {
741            self.tick();
742        } else {
743            for _ in 0..(delta_ms / self.ms_per_tick as u32) {
744                self.tick();
745            }
746        }
747    }
748
749    /// Handles input events.
750    ///
751    /// Discards the input event if the input queue is full.
752    ///
753    /// Returns the time in ms until the next scheduled event, if any.
754    ///  (Time until next tick, if any, will always be >0, so 0 can be used as "NO EVENTS")
755    pub fn handle_input_after_time(&mut self, delta_ms: u32, ev: input::Event) -> Option<u32> {
756        self.tick_by(delta_ms);
757        self.handle_input(ev);
758        let next_event_time = self.event_scheduler.next_event_time();
759        debug_assert!(next_event_time != Some(0));
760        next_event_time
761    }
762
763    /// If the event scheduler has a next scheduled event,
764    ///  it ticks the keymap forward to that event,
765    ///  returning the time in ms until the following event.
766    ///
767    /// Otherwise, does nothing and returns None.
768    pub fn tick_to_next_scheduled_event(&mut self) -> Option<u32> {
769        if let Some(delta_ms) = self.event_scheduler.next_event_time() {
770            self.tick_by(delta_ms);
771            self.event_scheduler.next_event_time()
772        } else {
773            None
774        }
775    }
776
777    /// Updates the keymap indicating a report is sent; returns the reportable keymap output.
778    pub fn report_output(&mut self) -> KeymapOutput {
779        self.hid_reporter.update(self.pressed_keys());
780        self.hid_reporter.report_sent();
781
782        KeymapOutput::new(self.hid_reporter.reportable_key_outputs())
783    }
784
785    /// Returns the current HID keyboard report.
786    #[doc(hidden)]
787    pub fn boot_keyboard_report(&self) -> [u8; 8] {
788        KeymapOutput::new(self.pressed_keys()).as_hid_boot_keyboard_report()
789    }
790
791    /// Whether the keymap has pending state that requires polling.
792    pub fn requires_polling(&self) -> bool {
793        !self.event_scheduler.pending_events.is_empty()
794            || !self.input_queue.is_empty()
795            || self
796                .pending_state
797                .as_ref()
798                .is_some_and(|ps| !ps.ingest_queue.is_empty())
799    }
800
801    #[doc(hidden)]
802    pub fn has_scheduled_events(&self) -> bool {
803        !self.event_scheduler.pending_events.is_empty()
804            || !self.event_scheduler.scheduled_events.is_empty()
805            || !self.input_queue.is_empty()
806            || self
807                .pending_state
808                .as_ref()
809                .is_some_and(|ps| !ps.ingest_queue.is_empty())
810    }
811}
812
813/// Test-only inspection hooks for pending-state / input-queue pacing.
814///
815/// Used by `smart-keymap-full-system-std` integration tests (cannot live as
816/// `#[cfg(test)]` unit helpers because that crate is a separate package).
817#[cfg(feature = "std")]
818#[doc(hidden)]
819impl<
820        I: Debug + Index<usize, Output = R>,
821        R: Copy + Debug,
822        Ctx: Debug + key::Context<Event = Ev> + SetKeymapContext,
823        Ev: Copy + Debug,
824        PKS: Debug,
825        KS: Copy + Debug + From<key::NoOpKeyState>,
826        S: key::System<R, Ref = R, Context = Ctx, Event = Ev, PendingKeyState = PKS, KeyState = KS>,
827    > Keymap<I, R, Ctx, Ev, PKS, KS, S>
828{
829    /// Whether a pending key state is active.
830    pub fn test_is_pending(&self) -> bool {
831        self.pending_state.is_some()
832    }
833
834    /// Length of the pending session log, if pending.
835    pub fn test_pending_queued_events_len(&self) -> Option<usize> {
836        self.pending_state
837            .as_ref()
838            .map(|pending_state| pending_state.queued_events.len())
839    }
840
841    /// Session-log input events (only `Event::Input` variants),
842    ///  in log order.
843    pub fn test_pending_session_log_inputs(&self) -> Option<heapless::Vec<input::Event, 16>> {
844        self.pending_state.as_ref().map(|pending_state| {
845            let mut inputs = heapless::Vec::new();
846            for ev in pending_state.queued_events.iter() {
847                if let key::Event::Input(ie) = ev {
848                    let _ = inputs.push(*ie);
849                }
850            }
851            inputs
852        })
853    }
854
855    /// Length of the active delay line
856    ///  (pending `ingest_queue` while pending, else global `input_queue`).
857    pub fn test_input_queue_len(&self) -> usize {
858        if let Some(pending_state) = self.pending_state.as_ref() {
859            pending_state.ingest_queue.len()
860        } else {
861            self.input_queue.len()
862        }
863    }
864
865    /// Delay gate of the active delay line
866    ///  (pending `ingest_queue` while pending, else global `input_queue`).
867    pub fn test_input_queue_delay(&self) -> bool {
868        if let Some(pending_state) = self.pending_state.as_ref() {
869            pending_state.ingest_queue.delay()
870        } else {
871            self.input_queue.delay()
872        }
873    }
874
875    /// Schedule an immediate key event and process pending events.
876    pub fn test_handle_scheduled_key_event(&mut self, ev: key::Event<Ev>) {
877        self.event_scheduler
878            .schedule_event(key::ScheduledEvent::immediate(ev));
879        self.handle_pending_events();
880    }
881}
882
883#[cfg(test)]
884#[allow(clippy::unwrap_used, clippy::expect_used)]
885mod tests {
886    use super::*;
887
888    #[test]
889    fn test_keymap_output_pressed_key_codes_includes_modifier_key_code() {
890        // Assemble - include modifier key left ctrl
891        let mut input: heapless::Vec<key::KeyOutput, { MAX_PRESSED_KEYS }> = heapless::Vec::new();
892        input.push(key::KeyOutput::from_key_code(0x04)).unwrap();
893        input.push(key::KeyOutput::from_key_code(0xE0)).unwrap();
894
895        // Act - construct the output
896        let keymap_output = KeymapOutput::new(input);
897        let pressed_key_codes = keymap_output.pressed_key_codes();
898
899        // Assert - check the 0xE0 gets included as a key code.
900        assert!(pressed_key_codes.contains(&0xE0))
901    }
902
903    #[test]
904    fn test_keymap_output_as_hid_boot_keyboard_report_gathers_modifiers() {
905        // Assemble - include modifier key left ctrl
906        let mut input: heapless::Vec<key::KeyOutput, { MAX_PRESSED_KEYS }> = heapless::Vec::new();
907        input.push(key::KeyOutput::from_key_code(0x04)).unwrap();
908        input.push(key::KeyOutput::from_key_code(0xE0)).unwrap();
909
910        // Act - construct the output
911        let keymap_output = KeymapOutput::new(input);
912        let actual_report: [u8; 8] = keymap_output.as_hid_boot_keyboard_report();
913
914        // Assert - check the 0xE0 gets considered as a "modifier".
915        let expected_report: [u8; 8] = [0x01, 0, 0x04, 0, 0, 0, 0, 0];
916        assert_eq!(expected_report, actual_report);
917    }
918
919    #[test]
920    fn test_keymap_output_pressed_consumer_codes() {
921        let mut input: heapless::Vec<key::KeyOutput, { MAX_PRESSED_KEYS }> = heapless::Vec::new();
922        input
923            .push(key::KeyOutput::from_consumer_code(0xE9))
924            .unwrap();
925
926        let keymap_output = KeymapOutput::new(input);
927        assert_eq!(
928            heapless::Vec::<u8, 24>::from_slice(&[0xE9]).unwrap(),
929            keymap_output.pressed_consumer_codes()
930        );
931    }
932
933    #[test]
934    fn test_keymap_output_pressed_mouse_output_combines_buttons() {
935        let mut input: heapless::Vec<key::KeyOutput, { MAX_PRESSED_KEYS }> = heapless::Vec::new();
936        input
937            .push(key::KeyOutput::from_mouse_output(key::MouseOutput {
938                pressed_buttons: 0b001,
939                ..key::MouseOutput::NO_OUTPUT
940            }))
941            .unwrap();
942        input
943            .push(key::KeyOutput::from_mouse_output(key::MouseOutput {
944                pressed_buttons: 0b010,
945                ..key::MouseOutput::NO_OUTPUT
946            }))
947            .unwrap();
948
949        let keymap_output = KeymapOutput::new(input);
950        assert_eq!(
951            key::MouseOutput {
952                pressed_buttons: 0b011,
953                ..key::MouseOutput::NO_OUTPUT
954            },
955            keymap_output.pressed_mouse_output()
956        );
957    }
958
959    #[test]
960    fn test_keymap_context_default_is_zeroed() {
961        let context = KeymapContext::new();
962        assert_eq!(0, context.time_ms);
963        assert_eq!(0, context.idle_time_ms);
964    }
965}