From 537b9aaa569e42e0e9a61190b2b3a2d92a09ea18 Mon Sep 17 00:00:00 2001 From: Haobo Gu Date: Thu, 16 Nov 2023 20:32:37 +0800 Subject: [PATCH] feat(core): rework on internal representations of key actions Signed-off-by: Haobo Gu --- rmk/src/action.rs | 232 ++++++++++++++++++++++++++++++---------------- 1 file changed, 150 insertions(+), 82 deletions(-) diff --git a/rmk/src/action.rs b/rmk/src/action.rs index f13f217e..cccbfaec 100644 --- a/rmk/src/action.rs +++ b/rmk/src/action.rs @@ -1,27 +1,27 @@ use crate::keycode::{KeyCode, ModifierCombination}; +use log::{error, warn}; +use num_enum::FromPrimitive; +use packed_struct::PackedStructSlice; -/// A KeyAction is the action of a keyboard position, stored in keymap. -/// It can be a single action like triggering a key, or a composite keyboard action like TapHold +/// A KeyAction is the action at a keyboard position, stored in keymap. +/// It can be a single action like triggering a key, or a composite keyboard action like tap/hold /// -/// Each `KeyAction` can be serialized to a u16, which can be stored in EEPROM. +/// Each `KeyAction` can be serialized to a u16 action code. There are 2 patterns of action code's bit-field composition of `KeyAction`: /// -/// 16bits = KeyAction type(3bits) + Layer/Modifier Detail(5bits) + BasicAction(8bits) +/// - KeyActionType(8bits) + BasicAction(8bits) /// -/// OR +/// - KeyActionType(4bits) + Action(12bits) /// -/// 16bits = KeyAction type(3bits) + Action(12bits) +/// The `BasicAction` represents only a single key action of keycodes defined in HID spec. The `Action` represents all actions defined in the following `Action` enum, including modifier combination and layer switch. +/// +/// The KeyActionType bits varies between different types of a KeyAction, see docs of each enum variant. #[derive(Debug, Copy, Clone, PartialEq, Eq)] pub enum KeyAction { - /// No action - /// - /// Serialized as 0x0000 + /// No action. Serialized as 0x0000. No, - /// Transparent action, next layer will be checked - /// - /// Serialized as 0x0001 + /// Transparent action, next layer will be checked. Serialized as 0x0001. Transparent, - /// A single action, such as triggering a key, or activating a layer. - /// Action is triggered when pressed and cancelled when released. + /// A single action, such as triggering a key, or activating a layer. Action is triggered when pressed and cancelled when released. /// /// Serialized as 0000|Action(12bits). Single(Action), @@ -33,72 +33,89 @@ pub enum KeyAction { /// /// Serialized as 0010|Action(12bits). OneShot(Action), - /// Action with a modifier triggered, only `Action::Key(BasicKeyCodes)`(aka 0x004 ~ 0x0FF) can be used with modifier. + /// Layer tap/hold will trigger different actions: tap for basic action, hold for layer activation. + /// + /// Serialized as 0011|layer(4bits)|BasicAction(8bits). + LayerTapHold(Action, u8), + /// Action with the modifier combination triggered. /// /// Serialized as 010|modifier(5bits)|BasicAction(8bits). WithModifier(Action, ModifierCombination), - /// Layer Tap/hold will trigger different actions: TapHold(tap_action, hold_action). - /// Only modifier and layer operation(0~15 layer) can be used as `hold_action`. - /// `tap_action` is limited to `Action::Key(BasicKeyCodes)`(aka 0x004 ~ 0x0FF) - /// - /// Serialized as 1|layer(3bits)|Action(12bits) - LayerTapHold(Action, u8), - /// Modifier Tap/hold will trigger different actions: TapHold(tap_action, modifier). + /// Modifier tap/hold will trigger different actions: tap for basic action, hold for modifier activation. /// - /// Serialized as 011|modifier(5bits)|BasicKeyCodes(8bits) + /// Serialized as 011|modifier(5bits)|BasicAction(8bits). ModifierTapHold(Action, ModifierCombination), - /// General TapHold action. It cannot be serialized to u16, will be ignored temporarily. - /// TODO: Figure out a better way to represent & save a general tap/hold action + /// General tap/hold action. Because current BaseAction actually uses at most 7 bits, so we borrow 1 bit as the identifier of general tap/hold action. + /// + /// Serialized as 1|BasicAction(7bits)|BasicAction(8bits). TapHold(Action, Action), } impl KeyAction { - // FIXME: remove it later - // Depreciated, uses to_via_keycode - // pub fn to_u16(&self) -> u16 { - // match self { - // KeyAction::No => 0x0000, - // KeyAction::Transparent => 0x0001, - // KeyAction::Single(a) => a.to_u16(), - // KeyAction::Tap(a) => 0x0001 | a.to_u16(), - // KeyAction::OneShot(a) => 0x0010 | a.to_u16(), - // KeyAction::WithModifier(a, m) => { - // let mut modifier_bits = [0]; - // // Ignore packing error - // ModifierCombination::pack_to_slice(m, &mut modifier_bits).unwrap_or_default(); - // 0x4000 | ((modifier_bits[0] as u16) << 8) | a.to_u16() - // } - // KeyAction::ModifierTapHold(action, modifier) => match action { - // Action::Key(k) => { - // if k.is_basic() { - // let mut modifier_bits = [0]; - // // Ignore packing error - // ModifierCombination::pack_to_slice(modifier, &mut modifier_bits) - // .unwrap_or_default(); - // 0x6000 | ((modifier_bits[0] as u16) << 8) | *k as u16 - // } else { - // 0x000 - // } - // } - // _ => { - // error!("ModifierTapHold supports basic keycodes"); - // 0x0000 - // } - // }, - // KeyAction::LayerTapHold(action, layer) => { - // if *layer < 8 { - // 0x8000 | ((*layer as u16) << 15) | action.to_u16() - // } else { - // error!("LayerTapHold supports layers 0~7, got {}", layer); - // 0x0000 - // } - // } - // KeyAction::TapHold(_tap, _hold) => { - // error!("Unsupported TapHold action: {:?}", self); - // 0x0000 - // } - // } - // } + /// Convert a `KeyAction` to corresponding key action code. + pub fn to_key_action_code(&self) -> u16 { + match self { + KeyAction::No => 0x0000, + KeyAction::Transparent => 0x0001, + KeyAction::Single(a) => a.to_action_code(), + KeyAction::Tap(a) => 0x0001 | a.to_action_code(), + KeyAction::OneShot(a) => 0x0010 | a.to_action_code(), + KeyAction::WithModifier(a, m) => { + let mut modifier_bits = [0]; + // Ignore packing error + ModifierCombination::pack_to_slice(m, &mut modifier_bits).unwrap_or_default(); + 0x4000 | ((modifier_bits[0] as u16) << 8) | a.to_basic_action_code() + } + KeyAction::ModifierTapHold(action, modifier) => { + let mut modifier_bits = [0]; + // Ignore packing error + ModifierCombination::pack_to_slice(modifier, &mut modifier_bits) + .unwrap_or_default(); + 0x6000 | ((modifier_bits[0] as u16) << 8) | action.to_basic_action_code() + } + KeyAction::LayerTapHold(action, layer) => { + if *layer < 16 { + 0x3000 | ((*layer as u16) << 15) | action.to_basic_action_code() + } else { + error!("LayerTapHold supports only layer 0~15, got {}", layer); + 0x0000 + } + } + KeyAction::TapHold(tap, hold) => { + 0x8000 | (hold.to_basic_action_code() << 15) | tap.to_basic_action_code() + } + } + } + + pub fn from_key_action_code(code: u16) -> KeyAction { + match code { + 0x0..=0xFFF => KeyAction::Single(Action::from_action_code(code)), + 0x1000..=0x1FFF => KeyAction::Tap(Action::from_action_code(code & 0xFFF)), + 0x2000..=0x2FFF => KeyAction::OneShot(Action::from_action_code(code & 0xFFF)), + 0x3000..=0x3FFF => { + let layer = (code >> 8) & 0xF; + KeyAction::LayerTapHold(Action::from_action_code(code & 0xFF), layer as u8) + } + 0x4000..=0x5FFF => { + let modifier_bits = (code >> 8) & 0x1F; + KeyAction::WithModifier( + Action::from_action_code(code & 0xFF), + ModifierCombination::from_bits(modifier_bits as u8), + ) + } + 0x6000..=0x7FFF => { + let modifier_bits = (code >> 8) & 0x1F; + KeyAction::ModifierTapHold( + Action::from_action_code(code & 0xFF), + ModifierCombination::from_bits(modifier_bits as u8), + ) + } + 0x8000..=0xFFFF => KeyAction::TapHold( + Action::from_action_code(code & 0xFF), + Action::from_action_code((code >> 8) & 0x7F), + ), + } + } } /// A single basic action that a keyboard can execute. @@ -106,26 +123,77 @@ impl KeyAction { #[derive(Debug, Copy, Clone, PartialEq, Eq)] pub enum Action { /// A normal key stroke, uses for all keycodes defined in `KeyCode` enum, including mouse key, consumer/system control, etc. + /// + /// Uses 0x000 ~ 0xCFF Key(KeyCode), - /// Modifier Combination, used for oneshot keyaction + /// Modifier Combination, used for oneshot keyaction. + /// + /// Uses 0xE00 ~ 0xE1F. Serialized as 1110|000|modifier(5bits) Modifier(ModifierCombination), /// Activate a layer + /// + /// Uses 0xE20 ~ 0xE3F. Serialized as 1110|001|layer_num(5bits) LayerOn(u8), /// Deactivate a layer + /// + /// Uses 0xE40 ~ 0xE5F. Serialized as 1110|010|layer_num(5bits) LayerOff(u8), /// Toggle a layer + /// + /// Uses 0xE60 ~ 0xE7F. Serialized as 1110|011|layer_num(5bits) LayerToggle(u8), } impl Action { - // FIXME: remove it later - // pub fn to_u16(&self) -> u16 { - // match self { - // Action::Key(k) => *k as u16, - // Action::LayerOn(layer) => 0x5100 & (*layer as u16), - // Action::LayerOff(layer) => *layer as u16, - // Action::LayerToggle(layer) => *layer as u16, - // Action::Modifier(m) => m.to_bits() as u16, - // } - // } + /// Convert an `Action` to 12-bit action code + pub fn to_action_code(&self) -> u16 { + match self { + Action::Key(k) => *k as u16, + Action::Modifier(m) => 0xE00 | (m.to_bits() as u16), + Action::LayerOn(layer) => 0xE20 | (*layer as u16), + Action::LayerOff(layer) => 0xE40 | (*layer as u16), + Action::LayerToggle(layer) => 0xE60 | (*layer as u16), + } + } + + /// Create an `Action` from action_code, returns Key(KeyCode::No) if the action code is not valid. + pub fn from_action_code(action_code: u16) -> Action { + match action_code { + 0x000..=0xCFF => Action::Key(KeyCode::from_primitive(action_code)), + 0xE00..=0xE1F => { + let modifier_bits = (action_code & 0xFF) as u8; + Action::Modifier(ModifierCombination::from_bits(modifier_bits)) + } + 0xE20..=0xE3F => { + let layer = (action_code & 0xFF) as u8; + Action::LayerOn(layer) + } + 0xE40..=0xE5F => { + let layer = (action_code & 0xFF) as u8; + Action::LayerOff(layer) + } + 0xE60..=0xE7F => { + let layer = (action_code & 0xFF) as u8; + Action::LayerToggle(layer) + } + _ => { + warn!("Not a valid 12-bit action code: {:X}", action_code); + Action::Key(KeyCode::No) + } + } + } + + /// Convert an `Action` to 8-bit basic action code, only applicable for `Key(BasicKeyCode)` + pub fn to_basic_action_code(&self) -> u16 { + match self { + Action::Key(kc) => { + if kc.is_basic() { + *kc as u16 + } else { + 0 + } + } + _ => 0, + } + } }