use crate::keycode::KeyCode;
use crate::modifier::ModifierCombination;
#[derive(Clone, Copy, Debug, serde::Serialize, serde::Deserialize)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[derive(postcard::experimental::max_size::MaxSize)]
pub struct EncoderAction {
clockwise: KeyAction,
counter_clockwise: KeyAction,
}
impl Default for EncoderAction {
fn default() -> Self {
Self {
clockwise: KeyAction::No,
counter_clockwise: KeyAction::No,
}
}
}
impl EncoderAction {
pub const fn new(clockwise: KeyAction, counter_clockwise: KeyAction) -> Self {
Self {
clockwise,
counter_clockwise,
}
}
pub fn set_clockwise(&mut self, clockwise: KeyAction) {
self.clockwise = clockwise;
}
pub fn set_counter_clockwise(&mut self, counter_clockwise: KeyAction) {
self.counter_clockwise = counter_clockwise;
}
pub fn clockwise(&self) -> KeyAction {
self.clockwise
}
pub fn counter_clockwise(&self) -> KeyAction {
self.counter_clockwise
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[repr(u8)]
pub enum MorseMode {
PermissiveHold,
HoldOnOtherPress,
Normal,
}
#[derive(PartialEq, Eq, Clone, Copy, Debug, serde::Serialize, serde::Deserialize)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[derive(postcard::experimental::max_size::MaxSize)]
pub struct MorseProfile(u32);
impl MorseProfile {
pub const fn const_default() -> Self {
Self(0)
}
pub fn unilateral_tap(self) -> Option<bool> {
match self.0 & 0x0000_C000 {
0x0000_C000 => Some(true),
0x0000_8000 => Some(false),
_ => None,
}
}
pub const fn with_unilateral_tap(self, b: Option<bool>) -> Self {
Self(
(self.0 & 0xFFFF_3FFF)
| match b {
Some(true) => 0x0000_C000,
Some(false) => 0x0000_8000,
None => 0,
},
)
}
pub fn mode(self) -> Option<MorseMode> {
match self.0 & 0xC000_0000 {
0xC000_0000 => Some(MorseMode::Normal),
0x8000_0000 => Some(MorseMode::HoldOnOtherPress),
0x4000_0000 => Some(MorseMode::PermissiveHold),
_ => None,
}
}
pub const fn with_mode(self, m: Option<MorseMode>) -> Self {
Self(
(self.0 & 0x3FFF_FFFF)
| match m {
Some(MorseMode::Normal) => 0xC000_0000,
Some(MorseMode::HoldOnOtherPress) => 0x8000_0000,
Some(MorseMode::PermissiveHold) => 0x4000_0000,
None => 0,
},
)
}
pub fn hold_timeout_ms(self) -> Option<u16> {
let t = (self.0 & 0x3FFF) as u16;
if t == 0 { None } else { Some(t) }
}
pub const fn with_hold_timeout_ms(self, t: Option<u16>) -> Self {
if let Some(t) = t {
Self((self.0 & 0xFFFF_C000) | (t as u32 & 0x3FFF))
} else {
Self(self.0 & 0xFFFF_C000)
}
}
pub const fn set_hold_timeout_ms(&mut self, t: u16) {
self.0 = (self.0 & 0xFFFF_C000) | (t as u32 & 0x3FFF)
}
pub const fn set_gap_timeout_ms(&mut self, t: u16) {
self.0 = (self.0 & 0xC000_FFFF) | ((t as u32 & 0x3FFF) << 16)
}
pub fn gap_timeout_ms(self) -> Option<u16> {
let t = ((self.0 >> 16) & 0x3FFF) as u16;
if t == 0 { None } else { Some(t) }
}
pub const fn with_gap_timeout_ms(self, t: Option<u16>) -> Self {
if let Some(t) = t {
Self((self.0 & 0xC000_FFFF) | ((t as u32 & 0x3FFF) << 16))
} else {
Self(self.0 & 0xC000_FFFF)
}
}
pub const fn new(
unilateral_tap: Option<bool>,
mode: Option<MorseMode>,
hold_timeout_ms: Option<u16>,
gap_timeout_ms: Option<u16>,
) -> Self {
let mut v = 0u32;
if let Some(t) = hold_timeout_ms {
v = (t & 0x3FFF) as u32;
}
if let Some(t) = gap_timeout_ms {
v |= ((t & 0x3FFF) as u32) << 16;
}
if let Some(b) = unilateral_tap {
v |= if b { 0x0000_C000 } else { 0x0000_8000 };
}
if let Some(m) = mode {
v |= match m {
MorseMode::Normal => 0xC000_0000,
MorseMode::HoldOnOtherPress => 0x8000_0000,
MorseMode::PermissiveHold => 0x4000_0000,
};
}
MorseProfile(v)
}
}
impl Default for MorseProfile {
fn default() -> Self {
MorseProfile::const_default()
}
}
impl From<u32> for MorseProfile {
fn from(v: u32) -> Self {
MorseProfile(v)
}
}
impl From<MorseProfile> for u32 {
fn from(val: MorseProfile) -> Self {
val.0
}
}
#[derive(Debug, Copy, Clone, Eq, serde::Serialize, serde::Deserialize)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[derive(postcard::experimental::max_size::MaxSize)]
pub enum KeyAction {
No,
Transparent,
Single(Action),
Tap(Action),
TapHold(Action, Action, MorseProfile),
Morse(u8),
}
impl KeyAction {
pub fn to_action(self) -> Action {
match self {
KeyAction::Single(a) | KeyAction::Tap(a) => a,
_ => Action::No,
}
}
pub fn is_morse(&self) -> bool {
matches!(self, KeyAction::TapHold(_, _, _) | KeyAction::Morse(_))
}
pub fn is_empty(&self) -> bool {
matches!(self, KeyAction::No)
}
}
impl PartialEq for KeyAction {
fn eq(&self, other: &Self) -> bool {
match (self, other) {
(KeyAction::No, KeyAction::No) => true,
(KeyAction::Transparent, KeyAction::Transparent) => true,
(KeyAction::Single(a), KeyAction::Single(b)) => a == b,
(KeyAction::Tap(a), KeyAction::Tap(b)) => a == b,
(KeyAction::TapHold(a, b, _), KeyAction::TapHold(c, d, _)) => a == c && b == d,
(KeyAction::Morse(a), KeyAction::Morse(b)) => a == b,
_ => false,
}
}
}
#[derive(Debug, Copy, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[derive(postcard::experimental::max_size::MaxSize)]
pub enum Action {
No,
Transparent,
Key(KeyCode),
Modifier(ModifierCombination),
KeyWithModifier(KeyCode, ModifierCombination),
LayerOn(u8),
LayerOnWithModifier(u8, ModifierCombination),
LayerOff(u8),
LayerToggle(u8),
DefaultLayer(u8),
LayerToggleOnly(u8),
TriggerMacro(u8),
OneShotLayer(u8),
OneShotModifier(ModifierCombination),
OneShotKey(KeyCode),
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_morse_profile_timeout_setters() {
let mut profile = MorseProfile::new(Some(true), Some(MorseMode::PermissiveHold), Some(1000), Some(2000));
assert_eq!(profile.hold_timeout_ms(), Some(1000));
assert_eq!(profile.gap_timeout_ms(), Some(2000));
assert_eq!(profile.unilateral_tap(), Some(true));
assert_eq!(profile.mode(), Some(MorseMode::PermissiveHold));
profile.set_hold_timeout_ms(1500);
assert_eq!(profile.hold_timeout_ms(), Some(1500));
assert_eq!(profile.gap_timeout_ms(), Some(2000));
assert_eq!(profile.unilateral_tap(), Some(true));
assert_eq!(profile.mode(), Some(MorseMode::PermissiveHold));
profile.set_gap_timeout_ms(2500);
assert_eq!(profile.hold_timeout_ms(), Some(1500));
assert_eq!(profile.gap_timeout_ms(), Some(2500));
assert_eq!(profile.unilateral_tap(), Some(true));
assert_eq!(profile.mode(), Some(MorseMode::PermissiveHold));
profile.set_hold_timeout_ms(0x3FFF);
profile.set_gap_timeout_ms(0x3FFF);
assert_eq!(profile.hold_timeout_ms(), Some(0x3FFF));
assert_eq!(profile.gap_timeout_ms(), Some(0x3FFF));
assert_eq!(profile.unilateral_tap(), Some(true));
assert_eq!(profile.mode(), Some(MorseMode::PermissiveHold));
profile.set_hold_timeout_ms(0);
profile.set_gap_timeout_ms(0);
assert_eq!(profile.hold_timeout_ms(), None);
assert_eq!(profile.gap_timeout_ms(), None);
assert_eq!(profile.unilateral_tap(), Some(true));
assert_eq!(profile.mode(), Some(MorseMode::PermissiveHold));
}
}