use crate::handlers::{OnOff, ProcessKeys, Action, HandlerResult};
use crate::key_codes::AcceptsKeycode;
use crate::key_stream::{iter_unhandled_mut, Event, EventStatus};
use crate::USBKeyOut;
use lazy_static::lazy_static;
use no_std_compat::prelude::v1::*;
use spin::RwLock;
#[repr(u8)]
#[derive(Debug)]
pub enum OneShotStatus {
Held,
HeldUsed,
Triggered,
TriggerUsed,
Off,
}
pub struct OneShot<M1, M2, M3> {
trigger1: u32,
trigger2: u32,
callbacks: M1,
on_double_tap_trigger1: M2,
on_double_tap_trigger2: M3,
status: OneShotStatus,
held_timeout: u16,
released_timeout: u16,
}
lazy_static! {
pub static ref ONESHOT_TRIGGERS: RwLock<Vec<u32>> = RwLock::new(Vec::new());
}
impl<M1: OnOff, M2: Action, M3: Action> OneShot<M1, M2, M3> {
pub fn new(
trigger1: impl AcceptsKeycode,
trigger2: impl AcceptsKeycode,
callbacks: M1,
on_double_tap_trigger1: M2,
on_double_tap_trigger2: M3,
held_timeout: u16,
released_timeout: u16,
) -> OneShot<M1, M2, M3> {
ONESHOT_TRIGGERS.write().push(trigger1.to_u32());
ONESHOT_TRIGGERS.write().push(trigger2.to_u32());
OneShot {
trigger1: trigger1.to_u32(),
trigger2: trigger2.to_u32(),
callbacks,
on_double_tap_trigger1,
on_double_tap_trigger2,
status: OneShotStatus::Off,
held_timeout,
released_timeout,
}
}
}
impl<T: USBKeyOut, M1: OnOff, M2: Action, M3: Action> ProcessKeys<T> for OneShot<M1, M2, M3> {
fn process_keys(&mut self, events: &mut Vec<(Event, EventStatus)>, output: &mut T) -> HandlerResult {
for (event, status) in iter_unhandled_mut(events) {
match event {
Event::KeyPress(kc) => {
if kc.keycode == self.trigger1 || kc.keycode == self.trigger2 {
*status = EventStatus::Handled;
match self.status {
OneShotStatus::Triggered => {
self.status = OneShotStatus::Off;
self.callbacks.on_deactivate(output);
if kc.keycode == self.trigger1 {
self.on_double_tap_trigger1.on_trigger(output);
} else if kc.keycode == self.trigger2 {
self.on_double_tap_trigger2.on_trigger(output);
}
}
OneShotStatus::Off => {
self.status = OneShotStatus::Held;
self.callbacks.on_activate(output)
}
OneShotStatus::Held
| OneShotStatus::HeldUsed
| OneShotStatus::TriggerUsed => {}
}
} else if !ONESHOT_TRIGGERS.read().contains(&kc.keycode) {
match self.status {
OneShotStatus::Triggered => self.status = OneShotStatus::TriggerUsed,
OneShotStatus::TriggerUsed => {
self.status = OneShotStatus::Off;
self.callbacks.on_deactivate(output)
}
_ => {}
}
}
}
Event::KeyRelease(kc) => {
if kc.keycode == self.trigger1 || kc.keycode == self.trigger2 {
match self.status {
OneShotStatus::Held => {
if self.held_timeout > 0 && kc.ms_since_last > self.held_timeout {
self.status = OneShotStatus::Off;
self.callbacks.on_deactivate(output)
} else {
self.status = OneShotStatus::Triggered;
}
}
OneShotStatus::HeldUsed => {
self.status = OneShotStatus::Off;
self.callbacks.on_deactivate(output)
}
_ => {}
}
*status = EventStatus::Handled;
} else if !ONESHOT_TRIGGERS.read().contains(&kc.keycode) {
match self.status {
OneShotStatus::Triggered => {
self.status = OneShotStatus::Off;
self.callbacks.on_deactivate(output)
}
OneShotStatus::Held => self.status = OneShotStatus::HeldUsed,
_ => {}
}
}
}
Event::TimeOut(ms) => {
if let OneShotStatus::Triggered = self.status {
if self.released_timeout > 0 && *ms >= self.released_timeout {
self.status = OneShotStatus::Off;
self.callbacks.on_deactivate(output)
}
}
}
}
}
HandlerResult::NoOp
}
}
#[cfg(test)]
mod tests {
use crate::handlers::{OneShot, USBKeyboard};
#[allow(unused_imports)]
use crate::key_codes::{KeyCode, UserKey};
#[allow(unused_imports)]
use crate::test_helpers::{check_output, KeyOutCatcher, PressCounter};
#[allow(unused_imports)]
use crate::{
Event, EventStatus, Keyboard, KeyboardState, ProcessKeys, USBKeyOut, UnicodeSendMode,
};
use crate::premade::ActionNone;
use alloc::sync::Arc;
#[allow(unused_imports)]
use no_std_compat::prelude::v1::*;
use spin::RwLock;
#[test]
fn test_oneshot() {
let counter = Arc::new(RwLock::new(PressCounter {
down_counter: 0,
up_counter: 0,
}));
let t = OneShot::new(UserKey::UK0, UserKey::UK1, counter.clone(), ActionNone{}, ActionNone{}, 0, 0);
let mut keyboard = Keyboard::new(KeyOutCatcher::new());
keyboard.add_handler(Box::new(t));
keyboard.add_handler(Box::new(USBKeyboard::new()));
for trigger in [UserKey::UK0, UserKey::UK1].iter() {
counter.write().down_counter = 0;
counter.write().up_counter = 0;
keyboard.output.clear();
keyboard.add_keypress(trigger, 0);
keyboard.handle_keys().unwrap();
dbg!(counter.read());
assert!(counter.read().down_counter == 1);
assert!(counter.read().up_counter == 0);
assert!(keyboard.events.is_empty());
check_output(&keyboard, &[&[KeyCode::H], &[]]);
keyboard.output.clear();
keyboard.add_keyrelease(trigger, 0);
keyboard.handle_keys().unwrap();
assert!(counter.read().down_counter == 1);
assert!(counter.read().up_counter == 0);
assert!(keyboard.events.is_empty());
keyboard.add_keypress(trigger, 0);
keyboard.handle_keys().unwrap();
assert!(counter.read().down_counter == 1);
assert!(counter.read().up_counter == 1);
assert!(keyboard.events.is_empty());
keyboard.add_keyrelease(trigger, 0);
keyboard.handle_keys().unwrap();
assert!(counter.read().down_counter == 1);
assert!(counter.read().up_counter == 1);
assert!(keyboard.events.is_empty());
keyboard.add_keypress(trigger, 0);
keyboard.handle_keys().unwrap();
assert!(counter.read().down_counter == 2);
assert!(counter.read().up_counter == 1);
assert!(keyboard.events.is_empty());
keyboard.add_keypress(KeyCode::A, 20);
keyboard.handle_keys().unwrap();
assert!(counter.read().down_counter == 2);
assert!(counter.read().up_counter == 1);
keyboard.add_keyrelease(KeyCode::A, 20);
keyboard.handle_keys().unwrap();
assert!(counter.read().down_counter == 2);
assert!(counter.read().up_counter == 1);
keyboard.add_keyrelease(trigger, 0);
keyboard.handle_keys().unwrap();
dbg!(&counter);
assert!(counter.read().down_counter == 2);
assert!(counter.read().up_counter == 2);
assert!(keyboard.events.is_empty());
keyboard.add_keypress(trigger, 0);
keyboard.handle_keys().unwrap();
assert!(counter.read().down_counter == 3);
assert!(counter.read().up_counter == 2);
assert!(keyboard.events.is_empty());
keyboard.add_keyrelease(trigger, 0);
keyboard.handle_keys().unwrap();
assert!(counter.read().down_counter == 3);
assert!(counter.read().up_counter == 2);
assert!(keyboard.events.is_empty());
keyboard.add_keypress(trigger, 0);
keyboard.handle_keys().unwrap();
assert!(counter.read().down_counter == 3);
assert!(counter.read().up_counter == 3);
assert!(keyboard.events.is_empty());
keyboard.add_keyrelease(trigger, 0);
keyboard.handle_keys().unwrap();
assert!(counter.read().down_counter == 3);
assert!(counter.read().up_counter == 3);
assert!(keyboard.events.is_empty());
}
counter.write().down_counter = 0;
counter.write().up_counter = 0;
keyboard.output.clear();
keyboard.add_keypress(UserKey::UK0, 0);
keyboard.handle_keys().unwrap();
dbg!(counter.read());
assert!(counter.read().down_counter == 1);
assert!(counter.read().up_counter == 0);
assert!(keyboard.events.is_empty());
check_output(&keyboard, &[&[KeyCode::H], &[]]);
keyboard.output.clear();
keyboard.add_keyrelease(UserKey::UK0, 0);
keyboard.handle_keys().unwrap();
assert!(counter.read().down_counter == 1);
assert!(counter.read().up_counter == 0);
assert!(keyboard.events.is_empty());
keyboard.add_keypress(UserKey::UK1, 0);
keyboard.handle_keys().unwrap();
assert!(counter.read().down_counter == 1);
assert!(counter.read().up_counter == 1);
assert!(keyboard.events.is_empty());
keyboard.add_keyrelease(UserKey::UK1, 0);
keyboard.handle_keys().unwrap();
assert!(counter.read().down_counter == 1);
assert!(counter.read().up_counter == 1);
assert!(keyboard.events.is_empty());
keyboard.add_keypress(UserKey::UK1, 0);
keyboard.handle_keys().unwrap();
assert!(counter.read().down_counter == 2);
assert!(counter.read().up_counter == 1);
assert!(keyboard.events.is_empty());
keyboard.add_keypress(KeyCode::A, 20);
keyboard.handle_keys().unwrap();
assert!(counter.read().down_counter == 2);
assert!(counter.read().up_counter == 1);
keyboard.add_keyrelease(KeyCode::A, 20);
keyboard.handle_keys().unwrap();
assert!(counter.read().down_counter == 2);
assert!(counter.read().up_counter == 1);
keyboard.add_keyrelease(UserKey::UK1, 0);
keyboard.handle_keys().unwrap();
assert!(counter.read().down_counter == 2);
assert!(counter.read().up_counter == 2);
assert!(keyboard.events.is_empty());
keyboard.add_keypress(UserKey::UK0, 0);
keyboard.handle_keys().unwrap();
assert!(counter.read().down_counter == 3);
assert!(counter.read().up_counter == 2);
assert!(keyboard.events.is_empty());
keyboard.add_keyrelease(UserKey::UK0, 0);
keyboard.handle_keys().unwrap();
assert!(counter.read().down_counter == 3);
assert!(counter.read().up_counter == 2);
assert!(keyboard.events.is_empty());
keyboard.add_keypress(UserKey::UK1, 0);
keyboard.handle_keys().unwrap();
assert!(counter.read().down_counter == 3);
assert!(counter.read().up_counter == 3);
assert!(keyboard.events.is_empty());
keyboard.add_keyrelease(UserKey::UK1, 0);
keyboard.handle_keys().unwrap();
assert!(counter.read().down_counter == 3);
assert!(counter.read().up_counter == 3);
assert!(keyboard.events.is_empty());
}
#[test]
fn test_oneshot_timeout() {
let counter = Arc::new(RwLock::new(PressCounter {
down_counter: 0,
up_counter: 0,
}));
let timeout = 1000;
let t = OneShot::new(UserKey::UK0, UserKey::UK1, counter.clone(), ActionNone{}, ActionNone{}, timeout, 0);
let mut keyboard = Keyboard::new(KeyOutCatcher::new());
keyboard.add_handler(Box::new(t));
keyboard.add_handler(Box::new(USBKeyboard::new()));
for trigger in [UserKey::UK0, UserKey::UK1].iter() {
counter.write().down_counter = 0;
counter.write().up_counter = 0;
keyboard.output.clear();
keyboard.add_keypress(trigger, 0);
keyboard.handle_keys().unwrap();
dbg!(counter.read());
assert!(counter.read().down_counter == 1);
assert!(counter.read().up_counter == 0);
assert!(keyboard.events.is_empty());
check_output(&keyboard, &[&[KeyCode::H], &[]]);
keyboard.output.clear();
keyboard.add_keyrelease(trigger, timeout + 1);
keyboard.handle_keys().unwrap();
assert!(counter.read().down_counter == 1);
assert!(counter.read().up_counter == 1);
assert!(keyboard.events.is_empty());
}
}
#[test]
fn test_oneshot_double_tap() {
use crate::key_codes::KeyCode::*;
use crate::handlers::Action;
use crate::test_helpers::Checks;
let counter = Arc::new(RwLock::new(PressCounter {
down_counter: 0,
up_counter: 0,
}));
let timeout = 1000;
struct MyAction {
keycode: KeyCode
}
impl Action for MyAction {
fn on_trigger(&mut self, output: &mut dyn USBKeyOut) {
output.send_keys(&[self.keycode]);
}
}
let t = OneShot::new(UserKey::UK0, UserKey::UK1, counter.clone(), MyAction{keycode: A}, MyAction{keycode:B}, timeout, 0);
let mut keyboard = Keyboard::new(KeyOutCatcher::new());
keyboard.add_handler(Box::new(t));
keyboard.add_handler(Box::new(USBKeyboard::new()));
let trigger = UserKey::UK0;
keyboard.pc(trigger, &[&[H], &[]]);
keyboard.rc(trigger, &[&[]]);
keyboard.pc(trigger, &[&[I], &[A], &[]]);
keyboard.rc(trigger, &[&[]]);
assert!(counter.read().down_counter == 1);
assert!(counter.read().up_counter == 1);
let trigger = UserKey::UK1;
keyboard.pc(trigger, &[&[H], &[]]);
keyboard.rc(trigger, &[&[]]);
keyboard.pc(trigger, &[&[I], &[B], &[]]);
keyboard.rc(trigger, &[&[]]);
assert!(counter.read().down_counter == 2);
assert!(counter.read().up_counter == 2);
}
}