diff --git a/rmk-macro/src/behavior.rs b/rmk-macro/src/behavior.rs index 3f20d125..af5c3a84 100644 --- a/rmk-macro/src/behavior.rs +++ b/rmk-macro/src/behavior.rs @@ -47,7 +47,11 @@ fn expand_combos(combos: &Option) -> proc_macro2::TokenStream { let combos = combos.combos.iter().map(|combo| { let actions = combo.actions.iter().map(|a| parse_key(a.to_owned())); let output = parse_key(combo.output.to_owned()); - quote! { ::rmk::combo::Combo::new([#(#actions),*], #output) } + let layer = match combo.layer { + Some(layer) => quote! { ::core::option::Option::Some(#layer) }, + None => quote! { ::core::option::Option::None }, + }; + quote! { ::rmk::combo::Combo::new([#(#actions),*], #output, #layer) } }); quote! { ::heapless::Vec::from_iter([#(#combos),*]) } diff --git a/rmk-macro/src/config/mod.rs b/rmk-macro/src/config/mod.rs index 6f614a5f..6527c220 100644 --- a/rmk-macro/src/config/mod.rs +++ b/rmk-macro/src/config/mod.rs @@ -162,6 +162,7 @@ pub struct CombosConfig { pub struct ComboConfig { pub actions: Vec, pub output: String, + pub layer: Option, } /// Configurations for split keyboards diff --git a/rmk-macro/src/keyboard_config.rs b/rmk-macro/src/keyboard_config.rs index 2575e89a..9df51da1 100644 --- a/rmk-macro/src/keyboard_config.rs +++ b/rmk-macro/src/keyboard_config.rs @@ -455,6 +455,12 @@ impl KeyboardConfig { if c.actions.len() > COMBO_MAX_LENGTH { return rmk_compile_error!(format!("keyboard.toml: number of keys in combo #{i} is greater than [behavior.combo.max_length]")); } + + if let Some(layer) = c.layer { + if layer >= layout.layers { + return rmk_compile_error!(format!("keyboard.toml: layer in combo #{i} is greater than [layout.layers]")); + } + } } } diff --git a/rmk/src/combo.rs b/rmk/src/combo.rs index 99b761ec..91fe1913 100644 --- a/rmk/src/combo.rs +++ b/rmk/src/combo.rs @@ -11,6 +11,7 @@ pub(crate) const COMBO_MAX_LENGTH: usize = 4; pub struct Combo { pub(crate) actions: Vec, pub(crate) output: KeyAction, + pub(crate) layer: Option, state: u8, } @@ -21,23 +22,43 @@ impl Default for Combo { } impl Combo { - pub fn new>(actions: I, output: KeyAction) -> Self { + pub fn new>( + actions: I, + output: KeyAction, + layer: Option, + ) -> Self { Self { actions: Vec::from_iter(actions), output, + layer, state: 0, } } pub fn empty() -> Self { - Self::new(Vec::::new(), KeyAction::No) + Self::new( + Vec::::new(), + KeyAction::No, + None, + ) } - pub(crate) fn update(&mut self, key_action: KeyAction, key_event: KeyEvent) -> bool { + pub(crate) fn update( + &mut self, + key_action: KeyAction, + key_event: KeyEvent, + active_layer: u8, + ) -> bool { if !key_event.pressed || key_action == KeyAction::No { return false; } + if let Some(layer) = self.layer { + if layer != active_layer { + return false; + } + } + let action_idx = self.actions.iter().position(|&a| a == key_action); if let Some(i) = action_idx { self.state |= 1 << i; diff --git a/rmk/src/keyboard.rs b/rmk/src/keyboard.rs index 1b50e4f9..f5a6a747 100644 --- a/rmk/src/keyboard.rs +++ b/rmk/src/keyboard.rs @@ -322,8 +322,9 @@ impl<'a, const ROW: usize, const COL: usize, const NUM_LAYER: usize> key_event: KeyEvent, ) -> Option { let mut is_combo_action = false; + let current_layer = self.keymap.borrow().get_activated_layer(); for combo in self.keymap.borrow_mut().combos.iter_mut() { - is_combo_action |= combo.update(key_action, key_event); + is_combo_action |= combo.update(key_action, key_event, current_layer); } if key_event.pressed && is_combo_action { diff --git a/rmk/src/keymap.rs b/rmk/src/keymap.rs index 5ecc60c6..eeaae666 100644 --- a/rmk/src/keymap.rs +++ b/rmk/src/keymap.rs @@ -245,7 +245,7 @@ impl<'a, const ROW: usize, const COL: usize, const NUM_LAYER: usize> KeyAction::No } - fn get_activated_layer(&self) -> u8 { + pub(crate) fn get_activated_layer(&self) -> u8 { for (layer_idx, _) in self.layers.iter().enumerate().rev() { if self.layer_state[layer_idx] || layer_idx as u8 == self.default_layer { return layer_idx as u8; diff --git a/rmk/src/storage/mod.rs b/rmk/src/storage/mod.rs index 559dacfc..dacbf3ad 100644 --- a/rmk/src/storage/mod.rs +++ b/rmk/src/storage/mod.rs @@ -187,7 +187,11 @@ impl Value<'_> for StorageData { ); } BigEndian::write_u16(&mut buffer[9..11], to_via_keycode(combo.output)); - Ok(11) + buffer[11] = match combo.layer { + Some(layer) => layer.to_be(), + None => u8::MAX, + }; + Ok(12) } StorageData::ConnectionType(ty) => { buffer[0] = StorageKeys::ConnectionType as u8; @@ -273,7 +277,7 @@ impl Value<'_> for StorageData { Ok(StorageData::MacroData(buf)) } StorageKeys::ComboData => { - if buffer.len() < 11 { + if buffer.len() < 12 { return Err(SerializationError::InvalidData); } let mut actions = [KeyAction::No; 4]; @@ -282,10 +286,12 @@ impl Value<'_> for StorageData { from_via_keycode(BigEndian::read_u16(&buffer[1 + i * 2..3 + i * 2])); } let output = from_via_keycode(BigEndian::read_u16(&buffer[9..11])); + let layer = u8::from_be(buffer[11]); Ok(StorageData::ComboData(ComboData { idx: 0, actions, output, + layer: (layer != u8::MAX).then_some(layer), })) } StorageKeys::ConnectionType => Ok(StorageData::ConnectionType(buffer[1])), @@ -353,6 +359,7 @@ pub(crate) struct ComboData { pub(crate) idx: usize, pub(crate) actions: [KeyAction; COMBO_MAX_LENGTH], pub(crate) output: KeyAction, + pub(crate) layer: Option, } pub(crate) struct Storage< @@ -707,7 +714,7 @@ impl idx: real_idx, actions, output, + layer: combos[real_idx].layer, })) .await; }