Skip to main content

intercept_bounce/
filter.rs

1// This module defines the core BounceFilter state and logic.
2// It has been refactored to be stateless regarding historical statistics,
3// focusing only on the information needed for the immediate bounce decision.
4
5pub mod keynames;
6pub mod stats;
7
8use crate::event::{self, is_key_event};
9use crate::logger::EventInfo;
10use input_linux_sys::{input_event, KEY_MAX};
11use std::time::Duration;
12
13// Constants for filter state size
14/// Number of key codes to track (0 to KEY_MAX inclusive).
15pub const FILTER_MAP_SIZE: usize = KEY_MAX as usize + 1;
16/// Number of key states (0=release, 1=press, 2=repeat).
17pub const NUM_KEY_STATES: usize = 3;
18
19/// Holds the minimal state required for bounce filtering decisions.
20///
21/// This struct only stores the timestamp (in microseconds) of the last *passed* event
22/// for each key code and value combination.
23pub struct BounceFilter {
24    // Stores the timestamp (in microseconds) of the last event that *passed* the filter
25    // for a given key code (index 0..KEY_MAX) and key value (index 0=release, 1=press, 2=repeat).
26    // Initialized with u64::MAX to indicate no event has passed yet.
27    last_event_us: [[u64; NUM_KEY_STATES]; FILTER_MAP_SIZE],
28    // Ring buffer to store the last N *passed* events for debugging purposes.
29    // Only allocated if ring_buffer_size > 0.
30    recent_passed_events: Vec<Option<input_event>>,
31    recent_event_idx: usize,
32    ring_buffer_size: usize,
33    // Timestamp of the very first event processed, used for calculating total runtime.
34    overall_first_event_us: Option<u64>,
35    // Timestamp of the very last event processed, used for calculating total runtime.
36    overall_last_event_us: Option<u64>,
37}
38
39impl Default for BounceFilter {
40    fn default() -> Self {
41        // Default to a filter with no ring buffer
42        Self::new(0)
43    }
44}
45
46impl BounceFilter {
47    /// Creates a new `BounceFilter` with a specified ring buffer size.
48    ///
49    /// The ring buffer stores the last `ring_buffer_size` passed events for debugging.
50    /// If `ring_buffer_size` is 0, the buffer is not allocated and has no overhead.
51    #[must_use]
52    pub fn new(ring_buffer_size: usize) -> Self {
53        let recent_passed_events = if ring_buffer_size > 0 {
54            vec![None; ring_buffer_size]
55        } else {
56            Vec::new() // Don't allocate if size is 0
57        };
58
59        BounceFilter {
60            last_event_us: [[u64::MAX; NUM_KEY_STATES]; FILTER_MAP_SIZE],
61            recent_passed_events,
62            recent_event_idx: 0,
63            ring_buffer_size,
64            overall_first_event_us: None,
65            overall_last_event_us: None,
66        }
67    }
68
69    /// Checks an incoming event against the debounce filter state.
70    ///
71    /// Determines if the event is a bounce based on the `debounce_time_us`
72    /// and the timestamp of the last passed event of the same type.
73    /// Updates the internal state (`last_event_us`) *only* if the event passes.
74    /// Also tracks the overall first and last event timestamps.
75    ///
76    /// # Arguments
77    /// * `event`: The input event to check.
78    /// * `debounce_time`: The debounce threshold as a `Duration`.
79    ///
80    /// # Returns
81    /// An `EventInfo` struct containing the result of the check and relevant timestamps.
82    pub fn check_event(
83        &mut self,
84        event: &input_event,
85        debounce_time: Duration,
86        skip_debounce: bool,
87    ) -> EventInfo {
88        let event_us = event::event_microseconds(event);
89
90        // Update overall timestamps
91        if self.overall_first_event_us.is_none() {
92            self.overall_first_event_us = Some(event_us);
93        }
94        self.overall_last_event_us = Some(event_us);
95
96        if skip_debounce {
97            if self.ring_buffer_size > 0 {
98                self.recent_passed_events[self.recent_event_idx] = Some(*event);
99                self.recent_event_idx = (self.recent_event_idx + 1) % self.ring_buffer_size;
100            }
101            return EventInfo {
102                event: *event,
103                event_us,
104                is_bounce: false,
105                diff_us: None,
106                last_passed_us: None,
107            };
108        }
109
110        // --- Early returns for non-debounced events ---
111        // Pass non-key events or key repeats immediately
112        if !is_key_event(event) || event.value == 2 {
113            // Record passed event in ring buffer if enabled
114            if self.ring_buffer_size > 0 {
115                self.recent_passed_events[self.recent_event_idx] = Some(*event);
116                self.recent_event_idx = (self.recent_event_idx + 1) % self.ring_buffer_size;
117            }
118            return EventInfo {
119                event: *event,
120                event_us,
121                is_bounce: false,
122                diff_us: None,
123                last_passed_us: None, // No relevant last_passed_us for non-debounced events
124            };
125        }
126
127        // Check bounds for key code/value indices
128        let key_code_idx = event.code as usize;
129        let key_value_idx = event.value as usize;
130        if !(key_code_idx < FILTER_MAP_SIZE && key_value_idx < NUM_KEY_STATES) {
131            // Out of bounds - treat as passed, no relevant history
132            // Record passed event in ring buffer if enabled
133            if self.ring_buffer_size > 0 {
134                self.recent_passed_events[self.recent_event_idx] = Some(*event);
135                self.recent_event_idx = (self.recent_event_idx + 1) % self.ring_buffer_size;
136            }
137            return EventInfo {
138                event: *event,
139                event_us,
140                is_bounce: false,
141                diff_us: None,
142                last_passed_us: None,
143            };
144        }
145
146        // --- Debounce logic ---
147        let last_passed_us = self.last_event_us[key_code_idx][key_value_idx];
148
149        // If no previous event passed for this key/value, it cannot be a bounce. Record and pass.
150        if last_passed_us == u64::MAX {
151            self.last_event_us[key_code_idx][key_value_idx] = event_us;
152            // Record passed event in ring buffer if enabled
153            if self.ring_buffer_size > 0 {
154                self.recent_passed_events[self.recent_event_idx] = Some(*event);
155                self.recent_event_idx = (self.recent_event_idx + 1) % self.ring_buffer_size;
156            }
157            return EventInfo {
158                event: *event, // Copy event
159                event_us,
160                is_bounce: false,
161                diff_us: None,
162                last_passed_us: None, // No previous passed event for this key/value
163            };
164        }
165
166        // Calculate time difference if possible (handles time going backwards)
167        let diff_us_opt = event_us.checked_sub(last_passed_us);
168
169        if let Some(diff_us) = diff_us_opt {
170            // Check if the difference is within the debounce window.
171            if debounce_time > Duration::ZERO && Duration::from_micros(diff_us) < debounce_time {
172                // It's a bounce! Return bounce info. Do NOT update last_event_us or ring buffer.
173                return EventInfo {
174                    event: *event,
175                    event_us,
176                    is_bounce: true,
177                    diff_us: Some(diff_us),
178                    last_passed_us: Some(last_passed_us),
179                };
180            }
181        }
182        // If time went backwards (checked_sub returned None), or diff_us >= debounce_time, it's not a bounce.
183
184        // --- Event Passed ---
185        // If we reach here, the event is NOT a bounce. Record as passed.
186        self.last_event_us[key_code_idx][key_value_idx] = event_us;
187        // Record passed event in ring buffer if enabled
188        if self.ring_buffer_size > 0 {
189            self.recent_passed_events[self.recent_event_idx] = Some(*event);
190            self.recent_event_idx = (self.recent_event_idx + 1) % self.ring_buffer_size;
191        }
192
193        // Return non-bounce info, providing the timestamp of the previously passed event.
194        EventInfo {
195            event: *event,
196            event_us,
197            is_bounce: false,
198            diff_us: None, // Not a bounce, so no bounce diff_us
199            last_passed_us: Some(last_passed_us),
200        }
201    }
202
203    /// Returns the total duration based on the first and last event timestamps seen.
204    /// Returns `None` if no events were processed.
205    pub fn get_runtime_us(&self) -> Option<u64> {
206        self.overall_last_event_us.and_then(|last| {
207            self.overall_first_event_us
208                .map(|first| last.saturating_sub(first))
209        })
210    }
211}