darktimer/fw/dtlogic/src/input.rs

162 lines
5.2 KiB
Rust

#[derive(Clone)]
/// A stateful button debouncer.
pub struct Buttons {
state: [Option<u32>; 4],
}
impl Buttons {
pub fn new() -> Self {
Self {
state: [None; 4],
}
}
/// Update internal state from raw input state, with given millisecond timestamp. The input
/// state is proccessed/debounced into the internal state.
pub fn update(&mut self, ts: u32, state: [bool; 4]) {
for (&s, v) in state.iter().zip(self.state.iter_mut()) {
if let Some(pressed) = v {
// Ignore recent changes (ie., debounce).
if ts - *pressed < 10 {
continue
}
if !s {
*v = None;
}
} else {
if s {
*v = Some(ts);
}
}
}
}
/// Return a watcher, which is used to detect edge-triggered button events. Multiple
/// independent watchers can exist, eg. for different downstream code.
pub fn watch(&self) -> ButtonWatcher {
let mut pending = [false; 4];
for (i, v) in self.state.iter().enumerate() {
if v.is_some() {
pending[i] = true;
}
}
ButtonWatcher {
state: self.state.clone(),
pending,
}
}
}
/// A edge-triggered button event watcher. Updates events from a Buttons struct and yields button
/// press events once per button press.
pub struct ButtonWatcher {
state: [Option<u32>; 4],
pending: [bool; 4],
}
impl ButtonWatcher {
/// Update internal state from Buttons. If any events are missed, a best effort is made to
/// reconstruct missing presses - but for all presses to be available, they should be processed
/// as early as possible via the events function.
pub fn update(&mut self, buttons: &Buttons) {
for (i, (v, o)) in self.state.iter_mut().zip(buttons.state.iter()).enumerate() {
match (&v, o) {
(Some(a), Some(b)) => {
// Button continues to be pressed, or new press.
if a == b {
// Do nothing.
} else {
// New press, set pending and update.
self.pending[i] = true;
*v = Some(*b);
}
},
(Some(_), None) => {
// Button released.
*v = None;
},
(None, Some(b)) => {
// Button newly pressed.
self.pending[i] = true;
*v = Some(*b);
},
(None, None) => {
// Do nothing.
},
}
}
}
/// Returns an iterator over pending button presses. Button presses are registered as processed
/// one-by-one as the returned iterator is consumed.
pub fn events<'a>(&'a mut self) -> ButtonWatcherPending<'a> {
ButtonWatcherPending {
w: self,
}
}
}
/// Button press iterator, yielding a button number for each pending button press (edge-triggered).
pub struct ButtonWatcherPending<'a> {
w: &'a mut ButtonWatcher,
}
impl<'a> core::iter::Iterator for ButtonWatcherPending<'a> {
type Item = usize;
fn next(&mut self) -> Option<Self::Item> {
for (i, p) in self.w.pending.iter_mut().enumerate() {
if *p {
*p = false;
return Some(i);
}
}
None
}
}
#[cfg(test)]
mod tests {
use crate::input::Buttons;
#[test]
fn buttons_debounce() {
let mut b = Buttons::new();
b.update(100, [false, false, false, false]);
assert_eq!(b.state, [None, None, None, None]);
b.update(110, [true, false, true, false]);
assert_eq!(b.state, [Some(110), None, Some(110), None]);
b.update(110, [false, true, true, false]);
assert_eq!(b.state, [Some(110), Some(110), Some(110), None]);
b.update(112, [false, false, true, true]);
assert_eq!(b.state, [Some(110), Some(110), Some(110), Some(112)]);
b.update(120, [false, false, false, false]);
assert_eq!(b.state, [None, None, None, Some(112)]);
b.update(122, [false, false, false, true]);
assert_eq!(b.state, [None, None, None, Some(112)]);
b.update(182, [false, false, false, true]);
assert_eq!(b.state, [None, None, None, Some(112)]);
}
#[test]
fn buttons_watcher() {
let mut b = Buttons::new();
let mut w = b.watch();
assert_eq!(w.events().collect::<Vec<_>>(), vec![]);
b.update(100, [true, false, false, false]);
w.update(&b);
assert_eq!(w.events().collect::<Vec<_>>(), vec![0]);
b.update(120, [false, true, false, false]);
w.update(&b);
b.update(140, [false, false, true, false]);
w.update(&b);
assert_eq!(w.events().collect::<Vec<_>>(), vec![1, 2]);
assert_eq!(w.events().collect::<Vec<_>>(), vec![]);
b.update(150, [false, false, true, false]);
w.update(&b);
assert_eq!(w.events().collect::<Vec<_>>(), vec![]);
}
}