Skip to main content

smart_keymap_core/keymap/
observed_eb_keymap.rs

1use core::fmt::Debug;
2use core::ops::Index;
3
4use crate::input;
5use crate::key;
6use crate::keymap;
7
8use keymap::Keymap;
9use keymap::ReportHints;
10use keymap::SetKeymapContext;
11
12/// Upper bound on ticks when draining scheduled events in tests.
13///
14/// Prevents integration tests from hanging indefinitely if a key keeps
15/// rescheduling work (e.g. an automation / string-macro loop).
16pub const MAX_TICKS_UNTIL_NO_SCHEDULED_EVENTS: usize = 10_000;
17
18/// Wrapper around a [crate::keymap::Keymap] that also tracks distinct HID reports.
19#[derive(Debug)]
20pub struct ObservedKeymap<I: Index<usize, Output = R>, R, Ctx, Ev: Debug, PKS, KS, S> {
21    keymap: Keymap<I, R, Ctx, Ev, PKS, KS, S>,
22    distinct_reports: keymap::DistinctReports,
23}
24
25impl<
26        I: Debug + Index<usize, Output = R>,
27        R: Copy + Debug,
28        Ctx: Debug + key::Context<Event = Ev> + SetKeymapContext + ReportHints,
29        Ev: Copy + Debug,
30        PKS: Debug,
31        KS: Copy + Debug + From<key::NoOpKeyState>,
32        S: key::System<R, Ref = R, Context = Ctx, Event = Ev, PendingKeyState = PKS, KeyState = KS>,
33    > ObservedKeymap<I, R, Ctx, Ev, PKS, KS, S>
34{
35    /// Constructs an observed keymap with a new [keymap::DistinctReports].
36    pub fn new(keymap: Keymap<I, R, Ctx, Ev, PKS, KS, S>) -> Self {
37        ObservedKeymap {
38            keymap,
39            distinct_reports: keymap::DistinctReports::new(),
40        }
41    }
42
43    /// Proxies [keymap::Keymap::handle_input_after_time], `tick`'ing the keymap appropriately.
44    pub fn handle_input_after_time(&mut self, delta_ms: u32, ev: input::Event) -> Option<u32> {
45        let ObservedKeymap {
46            keymap,
47            distinct_reports,
48        } = self;
49
50        let next_ev = keymap.handle_input_after_time(delta_ms, ev);
51
52        distinct_reports.update(keymap.report_output().as_hid_boot_keyboard_report());
53
54        // Clear input-queue pacing so a follow-up handle_input can process next.
55        keymap.tick();
56        distinct_reports.update(keymap.report_output().as_hid_boot_keyboard_report());
57
58        next_ev
59    }
60
61    /// Proxies [keymap::Keymap::tick_to_next_scheduled_event], updating reports appropriately.
62    pub fn tick_to_next_scheduled_event(&mut self) -> Option<u32> {
63        let ObservedKeymap {
64            keymap,
65            distinct_reports,
66        } = self;
67
68        let next_ev = keymap.tick_to_next_scheduled_event();
69
70        distinct_reports.update(keymap.report_output().as_hid_boot_keyboard_report());
71
72        next_ev
73    }
74
75    /// Proxies [keymap::Keymap::boot_keyboard_report].
76    pub fn boot_keyboard_report(&self) -> [u8; 8] {
77        let ObservedKeymap { keymap, .. } = self;
78
79        keymap::KeymapOutput::new(keymap.pressed_keys()).as_hid_boot_keyboard_report()
80    }
81
82    /// Reference to distinct reports.
83    pub fn distinct_reports(&self) -> &keymap::DistinctReports {
84        &self.distinct_reports
85    }
86
87    /// Ticks the keymap until there are no scheduled events, updating reports appropriately.
88    ///
89    /// Panics if the keymap still has scheduled events after
90    /// `MAX_TICKS_UNTIL_NO_SCHEDULED_EVENTS` ticks (likely an infinite
91    /// reschedule loop).
92    pub fn tick_until_no_scheduled_events(&mut self) {
93        let ObservedKeymap {
94            keymap,
95            distinct_reports,
96        } = self;
97
98        for _ in 0..MAX_TICKS_UNTIL_NO_SCHEDULED_EVENTS {
99            if !keymap.has_scheduled_events() {
100                return;
101            }
102            keymap.tick();
103            distinct_reports.update(keymap.report_output().as_hid_boot_keyboard_report());
104        }
105        if !keymap.has_scheduled_events() {
106            return;
107        }
108        panic!(
109            "keymap did not become idle after {} ticks; possible infinite scheduled-event loop",
110            MAX_TICKS_UNTIL_NO_SCHEDULED_EVENTS
111        );
112    }
113
114    /// Proxies [keymap::Keymap::requires_polling].
115    pub fn requires_polling(&self) -> bool {
116        let ObservedKeymap { keymap, .. } = self;
117
118        keymap.requires_polling()
119    }
120
121    /// Proxies [keymap::Keymap::tick], updating reports appropriately.
122    pub fn tick(&mut self) {
123        let ObservedKeymap {
124            keymap,
125            distinct_reports,
126        } = self;
127
128        keymap.tick();
129        distinct_reports.update(keymap.report_output().as_hid_boot_keyboard_report());
130    }
131}