use std::collections::hash_map::Entry;
use std::collections::HashMap;
use std::hash::Hash;
use std::ops::Add;
use std::ops::AddAssign;
use std::ops::BitOrAssign;
use std::ops::Sub;
use std::time::Duration;
use std::time::Instant;
fn min_instant<I>(a: Option<I>, b: Option<I>) -> Option<I>
where
I: Copy + Ord,
{
match (a, b) {
(None, None) => None,
(Some(_instant), None) => a,
(None, Some(_instant)) => b,
(Some(instant1), Some(instant2)) => Some(instant1.min(instant2)),
}
}
#[derive(Clone, Copy, Debug)]
enum KeyState<I> {
Pressed {
pressed_at: I,
fire_count: usize,
},
Repeated {
pressed_at: I,
next_repeat: I,
fire_count: usize,
},
ReleasePending {
pressed_at: I,
fire_count: usize,
},
}
impl<I> KeyState<I>
where
I: Copy + Ord + Add<Duration, Output = I> + AddAssign<Duration> + Sub<Output = Duration>,
{
fn pressed(pressed_at: I) -> Self {
Self::Pressed {
pressed_at,
fire_count: 0,
}
}
fn on_press(&mut self, now: I) {
match self {
Self::Pressed { .. } | Self::Repeated { .. } => {
},
Self::ReleasePending { fire_count, .. } => {
*self = Self::Pressed {
pressed_at: now,
fire_count: *fire_count,
}
},
}
}
fn on_release(&mut self, now: I, timeout: Duration, interval: Duration) {
match self {
Self::Pressed {
pressed_at,
fire_count,
} => {
let next_repeat = *pressed_at + timeout;
if now >= next_repeat {
*self = Self::Repeated {
pressed_at: *pressed_at,
next_repeat,
fire_count: *fire_count + 1,
};
let () = self.on_release(now, timeout, interval);
} else {
*self = Self::ReleasePending {
pressed_at: *pressed_at,
fire_count: *fire_count + 1,
}
}
},
Self::Repeated {
pressed_at,
next_repeat,
fire_count,
} => {
let diff = now - (*next_repeat).min(now);
*fire_count += (diff.as_secs_f64() / interval.as_secs_f64()).trunc() as usize;
if now > *next_repeat {
*fire_count += 1;
}
*self = Self::ReleasePending {
pressed_at: *pressed_at,
fire_count: *fire_count,
}
},
Self::ReleasePending { .. } => {
debug_assert!(false, "released key was not pressed");
},
}
}
fn next_tick(&self) -> Option<I> {
match self {
Self::Pressed { pressed_at, .. } => Some(*pressed_at),
Self::Repeated {
pressed_at,
next_repeat,
fire_count,
} => {
if *fire_count > 0 {
Some(*pressed_at)
} else {
Some(*next_repeat)
}
},
Self::ReleasePending {
pressed_at,
fire_count,
} => {
if *fire_count > 0 {
Some(*pressed_at)
} else {
None
}
},
}
}
fn tick(&mut self, timeout: Duration, interval: Duration) {
match self {
Self::Pressed {
pressed_at,
fire_count,
} => {
if let Some(count) = fire_count.checked_sub(1) {
*fire_count = count;
} else {
*self = KeyState::Repeated {
pressed_at: *pressed_at,
next_repeat: *pressed_at + timeout,
fire_count: 0,
};
}
},
Self::Repeated {
next_repeat,
fire_count,
..
} => {
if let Some(count) = fire_count.checked_sub(1) {
*fire_count = count;
} else {
*next_repeat += interval;
}
},
Self::ReleasePending { fire_count, .. } => {
*fire_count = fire_count.saturating_sub(1);
},
}
}
}
#[derive(Debug)]
pub enum KeyRepeat {
Enabled,
Disabled,
}
#[derive(Debug)]
pub struct Keys<K, I = Instant> {
timeout: Duration,
interval: Duration,
pressed: HashMap<K, KeyState<I>>,
}
impl<K, I> Keys<K, I>
where
K: Eq + Hash,
I: Copy + Ord + Add<Duration, Output = I> + AddAssign<Duration> + Sub<Output = Duration>,
{
pub fn new(timeout: Duration, interval: Duration) -> Self {
Self {
timeout,
interval,
pressed: HashMap::new(),
}
}
fn on_key_event(&mut self, now: I, key: K, pressed: bool) {
match pressed {
false => match self.pressed.entry(key) {
Entry::Vacant(_vacancy) => {
},
Entry::Occupied(mut occupancy) => {
let state = occupancy.get_mut();
let () = state.on_release(now, self.timeout, self.interval);
},
},
true => match self.pressed.entry(key) {
Entry::Vacant(vacancy) => {
let _state = vacancy.insert(KeyState::pressed(now));
},
Entry::Occupied(mut occupancy) => {
let state = occupancy.get_mut();
let () = state.on_press(now);
},
},
}
}
pub fn on_key_press(&mut self, now: I, key: K) {
self.on_key_event(now, key, true)
}
pub fn on_key_release(&mut self, now: I, key: K) {
self.on_key_event(now, key, false)
}
pub fn tick<F, C>(&mut self, now: I, mut handler: F) -> (C, Option<I>)
where
F: FnMut(&K, &mut KeyRepeat) -> C,
C: Default + BitOrAssign,
{
let mut change = C::default();
let mut next_tick = None;
let () = self.pressed.retain(|key, key_state| loop {
if let Some(tick) = key_state.next_tick() {
if tick > now {
next_tick = min_instant(next_tick, Some(tick));
break true
}
let mut repeat = KeyRepeat::Enabled;
change |= handler(key, &mut repeat);
match repeat {
KeyRepeat::Disabled => break false,
KeyRepeat::Enabled => {
let () = key_state.tick(self.timeout, self.interval);
},
}
} else {
break false
}
});
(change, next_tick)
}
#[inline]
pub fn clear(&mut self) {
self.pressed.clear()
}
#[inline]
pub fn timeout(&self) -> Duration {
self.timeout
}
#[inline]
pub fn interval(&self) -> Duration {
self.interval
}
}
#[cfg(test)]
mod tests {
use super::*;
use std::cell::Cell;
use std::ops::BitOr;
type Key = char;
const SECOND: Duration = Duration::from_secs(1);
const TIMEOUT: Duration = Duration::from_secs(5);
const INTERVAL: Duration = Duration::from_secs(1);
#[derive(Clone, Copy, Debug, Default, PartialEq)]
enum Change {
#[default]
Unchanged,
Changed,
}
impl BitOr<Change> for Change {
type Output = Change;
fn bitor(self, rhs: Change) -> Self::Output {
match (self, rhs) {
(Self::Changed, _) | (_, Self::Changed) => Self::Changed,
(Self::Unchanged, Self::Unchanged) => Self::Unchanged,
}
}
}
impl BitOrAssign<Change> for Change {
fn bitor_assign(&mut self, rhs: Change) {
*self = *self | rhs;
}
}
#[test]
fn press_release_without_tick() {
let l_pressed = Cell::new(0);
let mut handler = |key: &Key, _repeat: &mut KeyRepeat| match key {
'l' => {
l_pressed.set(l_pressed.get() + 1);
Change::Changed
},
_ => Change::Unchanged,
};
let now = Instant::now();
let mut keys = Keys::<Key>::new(TIMEOUT, INTERVAL);
let () = keys.on_key_press(now, 'l');
let () = keys.on_key_release(now + 1 * SECOND, 'l');
let (change, tick) = keys.tick(now + 1 * SECOND, &mut handler);
assert_eq!(l_pressed.get(), 1);
assert_eq!(change, Change::Changed);
assert_eq!(tick, None);
let (change, tick) = keys.tick(now + 2 * SECOND, &mut handler);
assert_eq!(l_pressed.get(), 1);
assert_eq!(change, Change::Unchanged);
assert_eq!(tick, None);
}
#[test]
fn press_after_release_pending() {
let h_pressed = Cell::new(0);
let mut handler = |key: &Key, _repeat: &mut KeyRepeat| match key {
'h' => {
h_pressed.set(h_pressed.get() + 1);
Change::Changed
},
_ => Change::Unchanged,
};
let now = Instant::now();
let mut keys = Keys::<Key>::new(TIMEOUT, INTERVAL);
let () = keys.on_key_press(now, 'h');
let () = keys.on_key_release(now + 1 * SECOND, 'h');
let () = keys.on_key_press(now + 2 * SECOND, 'h');
let (change, tick) = keys.tick(now + 2 * SECOND, &mut handler);
assert_eq!(h_pressed.get(), 2);
assert_eq!(change, Change::Changed);
assert_eq!(tick, Some(now + 7 * SECOND));
let (change, tick) = keys.tick(now + 3 * SECOND, &mut handler);
assert_eq!(h_pressed.get(), 2);
assert_eq!(change, Change::Unchanged);
assert_eq!(tick, Some(now + 7 * SECOND));
}
#[test]
fn release_pending_after_repeat() {
let h_pressed = Cell::new(0);
let mut handler = |key: &Key, _repeat: &mut KeyRepeat| match key {
'h' => {
h_pressed.set(h_pressed.get() + 1);
Change::Changed
},
_ => Change::Unchanged,
};
let now = Instant::now();
let mut keys = Keys::<Key>::new(TIMEOUT, INTERVAL);
let () = keys.on_key_press(now, 'h');
let () = keys.on_key_release(now + 7 * SECOND, 'h');
let (change, tick) = keys.tick(now + 8 * SECOND, &mut handler);
assert_eq!(h_pressed.get(), 4);
assert_eq!(change, Change::Changed);
assert_eq!(tick, None);
}
#[test]
fn key_pressing() {
let enter_pressed = Cell::new(0);
let space_pressed = Cell::new(0);
let f_pressed = Cell::new(0);
let mut handler = |key: &Key, repeat: &mut KeyRepeat| match key {
'\n' => {
enter_pressed.set(enter_pressed.get() + 1);
Change::Changed
},
' ' => {
space_pressed.set(space_pressed.get() + 1);
Change::Changed
},
'f' => {
f_pressed.set(f_pressed.get() + 1);
*repeat = KeyRepeat::Disabled;
Change::Changed
},
_ => Change::Unchanged,
};
let mut keys = Keys::<Key>::new(TIMEOUT, INTERVAL);
let now = Instant::now();
let (change, tick) = keys.tick(now, &mut handler);
assert_eq!(change, Change::Unchanged);
assert_eq!(tick, None);
let () = keys.on_key_press(now, '\n');
let (change, tick) = keys.tick(now, &mut handler);
assert_eq!(enter_pressed.get(), 1);
assert_eq!(change, Change::Changed);
assert_eq!(tick, Some(now + 5 * SECOND));
let (change, tick) = keys.tick(now, &mut handler);
assert_eq!(enter_pressed.get(), 1);
assert_eq!(change, Change::Unchanged);
assert_eq!(tick, Some(now + 5 * SECOND));
let () = keys.on_key_press(now, '\n');
let (change, tick) = keys.tick(now + Duration::from_millis(500), &mut handler);
assert_eq!(enter_pressed.get(), 1);
assert_eq!(change, Change::Unchanged);
assert_eq!(tick, Some(now + 5 * SECOND));
let (change, tick) = keys.tick(now + 5 * SECOND, &mut handler);
assert_eq!(enter_pressed.get(), 2);
assert_eq!(change, Change::Changed);
assert_eq!(tick, Some(now + 6 * SECOND));
let () = keys.on_key_press(now + 5 * SECOND, 'f');
assert_eq!(f_pressed.get(), 0);
let (change, tick) = keys.tick(now + 8 * SECOND, &mut handler);
assert_eq!(enter_pressed.get(), 5);
assert_eq!(f_pressed.get(), 1);
assert_eq!(change, Change::Changed);
assert_eq!(tick, Some(now + 9 * SECOND));
assert_eq!(space_pressed.get(), 0);
let () = keys.on_key_press(now + 9 * SECOND, ' ');
let (change, tick) = keys.tick(now + 10 * SECOND, &mut handler);
assert_eq!(enter_pressed.get(), 7);
assert_eq!(space_pressed.get(), 1);
assert_eq!(f_pressed.get(), 1);
assert_eq!(change, Change::Changed);
assert_eq!(tick, Some(now + 11 * SECOND));
let (change, tick) = keys.tick(now + 15 * SECOND, &mut handler);
assert_eq!(enter_pressed.get(), 12);
assert_eq!(space_pressed.get(), 3);
assert_eq!(f_pressed.get(), 1);
assert_eq!(change, Change::Changed);
assert_eq!(tick, Some(now + 16 * SECOND));
let () = keys.on_key_release(now + 16 * SECOND, ' ');
let (change, tick) = keys.tick(now + 16 * SECOND, &mut handler);
assert_eq!(enter_pressed.get(), 13);
assert_eq!(space_pressed.get(), 3);
assert_eq!(f_pressed.get(), 1);
assert_eq!(change, Change::Changed);
assert_eq!(tick, Some(now + 17 * SECOND));
let () = keys.on_key_release(now + 17 * SECOND, '\n');
let (change, tick) = keys.tick(now + 17 * SECOND, &mut handler);
assert_eq!(enter_pressed.get(), 13);
assert_eq!(space_pressed.get(), 3);
assert_eq!(f_pressed.get(), 1);
assert_eq!(change, Change::Unchanged);
assert_eq!(tick, None);
}
}