Skip to content

Commit

Permalink
feat(core): rework on internal representations of key actions
Browse files Browse the repository at this point in the history
Signed-off-by: Haobo Gu <[email protected]>
  • Loading branch information
HaoboGu committed Nov 16, 2023
1 parent 8fcc8dc commit 537b9aa
Showing 1 changed file with 150 additions and 82 deletions.
232 changes: 150 additions & 82 deletions rmk/src/action.rs
Original file line number Diff line number Diff line change
@@ -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),
Expand All @@ -33,99 +33,167 @@ 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.
/// An Action can be represented in 12 bits, aka 0x000 ~ 0xFFF
#[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,
}
}
}

0 comments on commit 537b9aa

Please sign in to comment.