From d315db9fb0ec416c884c08d1fd30813d8ff014eb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C4=99drzej=20Boczar?= Date: Thu, 28 Jul 2022 11:12:04 +0200 Subject: [PATCH 1/3] [WIP] Use a trait for Layers to decrease generic parameters count --- src/layout.rs | 96 +++++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 77 insertions(+), 19 deletions(-) diff --git a/src/layout.rs b/src/layout.rs index b949f9d..fd1dc08 100644 --- a/src/layout.rs +++ b/src/layout.rs @@ -53,28 +53,86 @@ use heapless::Vec; use State::*; -/// The Layers type. -/// -/// `Layers` type is an array of layers which contain the description -/// of actions on the switch matrix. For example `layers[1][2][3]` -/// corresponds to the key on the first layer, row 2, column 3. -/// The generic parameters are in order: the number of columns, rows and layers, -/// and the type contained in custom actions. -pub type Layers = - [[[Action; C]; R]; L]; +// /// The Layers type. +// /// +// /// `Layers` type is an array of layers which contain the description +// /// of actions on the switch matrix. For example `layers[1][2][3]` +// /// corresponds to the key on the first layer, row 2, column 3. +// /// The generic parameters are in order: the number of columns, rows and layers, +// /// and the type contained in custom actions. +// pub type Layers = +// [[[Action; C]; R]; L]; /// The current event stack. /// /// Events can be retrieved by iterating over this struct and calling [Stacked::event]. type Stack = ArrayDeque<[Stacked; 16], arraydeque::behavior::Wrapping>; +/// xd +pub trait Layers { + /// xd + fn get_action(&self, layer: usize, row: u8, col: u8) -> Option<&Action>; + /// xd + fn n_layers(&self) -> usize; + /// xd + fn n_rows(&self) -> u8; + /// xd + fn n_cols(&self) -> u8; +} + +impl Layers for [[[Action; C]; R]; L] { + fn get_action(&self, layer: usize, row: u8, col: u8) -> Option<&Action> { + self.get(layer) + .and_then(|l| l.get(row as usize)) + .and_then(|r| r.get(col as usize)) + } + + fn n_layers(&self) -> usize { + L + } + + fn n_rows(&self) -> u8 { + R as u8 + } + + fn n_cols(&self) -> u8 { + C as u8 + } +} + +impl Layers for [&[&[Action]]] { + fn get_action(&self, layer: usize, row: u8, col: u8) -> Option<&Action> { + self.get(layer) + .and_then(|l| l.get(row as usize)) + .and_then(|r| r.get(col as usize)) + } + + fn n_layers(&self) -> usize { + self.len() + } + + fn n_rows(&self) -> u8 { + self.get(0) + .map(|l| l.len() as u8) + .unwrap_or(0) + } + + fn n_cols(&self) -> u8 { + self.get(0) + .and_then(|l| l.get(0)) + .map(|r| r.len() as u8) + .unwrap_or(0) + } +} + /// The layout manager. It takes `Event`s and `tick`s as input, and /// generate keyboard reports. -pub struct Layout +pub struct Layout where T: 'static, + L: Layers + 'static, { - layers: &'static [[[Action; C]; R]; L], + layers: &'static L, default_layer: usize, states: Vec, 64>, waiting: Option>, @@ -327,9 +385,13 @@ impl TapHoldTracker { } } -impl Layout { +impl Layout +where + T: 'static, + L: Layers + 'static, +{ /// Creates a new `Layout` object. - pub fn new(layers: &'static [[[Action; C]; R]; L]) -> Self { + pub fn new(layers: &'static L) -> Self { Self { layers, default_layer: 0, @@ -420,11 +482,7 @@ impl Layout &'static Action { use crate::action::Action::*; - let action = self - .layers - .get(layer) - .and_then(|l| l.get(coord.0 as usize)) - .and_then(|l| l.get(coord.1 as usize)); + let action = self.layers.get_action(layer, coord.0, coord.1); match action { None => &NoOp, Some(Trans) => { @@ -522,7 +580,7 @@ impl Layout Date: Thu, 28 Jul 2022 11:33:49 +0200 Subject: [PATCH 2/3] [WIP] Use associated type for Action in the Layers trait --- src/layout.rs | 87 ++++++++++++++++++++------------------------------- 1 file changed, 34 insertions(+), 53 deletions(-) diff --git a/src/layout.rs b/src/layout.rs index fd1dc08..7e59c5a 100644 --- a/src/layout.rs +++ b/src/layout.rs @@ -68,19 +68,27 @@ use State::*; /// Events can be retrieved by iterating over this struct and calling [Stacked::event]. type Stack = ArrayDeque<[Stacked; 16], arraydeque::behavior::Wrapping>; -/// xd -pub trait Layers { - /// xd - fn get_action(&self, layer: usize, row: u8, col: u8) -> Option<&Action>; - /// xd + +/// The Layers trait. +/// +/// `Layers` conceptually is an array of layers which contain the description +/// of actions on the switch matrix. For example `layers[1][2][3]` corresponds +/// to the key on the first layer, row 2, column 3. +pub trait Layers { + // type CustomAction = core::convert::Infallible; + /// Type used for the [`Action::Custom`] variant. + type CustomAction; + + /// Retrieve the action at given `layer` for key at `(row, col)`. + fn get_action(&self, layer: usize, row: u8, col: u8) -> Option<&Action>; + + /// Get the number of layers. fn n_layers(&self) -> usize; - /// xd - fn n_rows(&self) -> u8; - /// xd - fn n_cols(&self) -> u8; } -impl Layers for [[[Action; C]; R]; L] { +impl Layers for [[[Action; C]; R]; L] { + type CustomAction = T; + fn get_action(&self, layer: usize, row: u8, col: u8) -> Option<&Action> { self.get(layer) .and_then(|l| l.get(row as usize)) @@ -90,17 +98,11 @@ impl Layers for [[[Action< fn n_layers(&self) -> usize { L } - - fn n_rows(&self) -> u8 { - R as u8 - } - - fn n_cols(&self) -> u8 { - C as u8 - } } -impl Layers for [&[&[Action]]] { +impl Layers for [&[&[Action]]] { + type CustomAction = T; + fn get_action(&self, layer: usize, row: u8, col: u8) -> Option<&Action> { self.get(layer) .and_then(|l| l.get(row as usize)) @@ -110,32 +112,15 @@ impl Layers for [&[&[Action]]] { fn n_layers(&self) -> usize { self.len() } - - fn n_rows(&self) -> u8 { - self.get(0) - .map(|l| l.len() as u8) - .unwrap_or(0) - } - - fn n_cols(&self) -> u8 { - self.get(0) - .and_then(|l| l.get(0)) - .map(|r| r.len() as u8) - .unwrap_or(0) - } } /// The layout manager. It takes `Event`s and `tick`s as input, and /// generate keyboard reports. -pub struct Layout -where - T: 'static, - L: Layers + 'static, -{ +pub struct Layout { layers: &'static L, default_layer: usize, - states: Vec, 64>, - waiting: Option>, + states: Vec, 64>, + waiting: Option>, stacked: Stack, tap_hold_tracker: TapHoldTracker, } @@ -385,11 +370,7 @@ impl TapHoldTracker { } } -impl Layout -where - T: 'static, - L: Layers + 'static, -{ +impl Layout { /// Creates a new `Layout` object. pub fn new(layers: &'static L) -> Self { Self { @@ -405,7 +386,7 @@ where pub fn keycodes(&self) -> impl Iterator + '_ { self.states.iter().filter_map(State::keycode) } - fn waiting_into_hold(&mut self) -> CustomEvent { + fn waiting_into_hold(&mut self) -> CustomEvent { if let Some(w) = &self.waiting { let hold = w.hold; let coord = w.coord; @@ -418,7 +399,7 @@ where CustomEvent::NoEvent } } - fn waiting_into_tap(&mut self) -> CustomEvent { + fn waiting_into_tap(&mut self) -> CustomEvent { if let Some(w) = &self.waiting { let tap = w.tap; let coord = w.coord; @@ -428,7 +409,7 @@ where CustomEvent::NoEvent } } - fn drop_waiting(&mut self) -> CustomEvent { + fn drop_waiting(&mut self) -> CustomEvent { self.waiting = None; CustomEvent::NoEvent } @@ -438,7 +419,7 @@ where /// /// Returns the corresponding `CustomEvent`, allowing to manage /// custom actions thanks to the `Action::Custom` variant. - pub fn tick(&mut self) -> CustomEvent { + pub fn tick(&mut self) -> CustomEvent { self.states = self.states.iter().filter_map(State::tick).collect(); self.stacked.iter_mut().for_each(Stacked::tick); self.tap_hold_tracker.tick(); @@ -455,7 +436,7 @@ where }, } } - fn unstack(&mut self, stacked: Stacked) -> CustomEvent { + fn unstack(&mut self, stacked: Stacked) -> CustomEvent { use Event::*; match stacked.event { Release(i, j) => { @@ -480,7 +461,7 @@ where self.unstack(stacked); } } - fn press_as_action(&self, coord: (u8, u8), layer: usize) -> &'static Action { + fn press_as_action(&self, coord: (u8, u8), layer: usize) -> &'static Action { use crate::action::Action::*; let action = self.layers.get_action(layer, coord.0, coord.1); match action { @@ -497,10 +478,10 @@ where } fn do_action( &mut self, - action: &'static Action, + action: &'static Action, coord: (u8, u8), delay: u16, - ) -> CustomEvent { + ) -> CustomEvent { assert!(self.waiting.is_none()); use Action::*; match action { @@ -516,7 +497,7 @@ where || coord != self.tap_hold_tracker.coord || self.tap_hold_tracker.timeout == 0 { - let waiting: WaitingState = WaitingState { + let waiting: WaitingState = WaitingState { coord, timeout: *timeout, delay, From bb59f04c67349b251d41a250d59befe4fc539044 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C4=99drzej=20Boczar?= Date: Thu, 28 Jul 2022 12:09:27 +0200 Subject: [PATCH 3/3] [WIP] Fix and test usage of Layers as a slice --- src/chording.rs | 2 +- src/layout.rs | 91 ++++++++++++++++++++++++++++++++++++++++--------- src/lib.rs | 4 +++ 3 files changed, 80 insertions(+), 17 deletions(-) diff --git a/src/chording.rs b/src/chording.rs index 8e713ab..c85a6f8 100644 --- a/src/chording.rs +++ b/src/chording.rs @@ -29,7 +29,7 @@ /// // A count of 30 (ms) is a good default /// const DEBOUNCE_COUNT: u16 = 30; /// -/// pub static LAYERS: keyberon::layout::Layers<3, 1, 1> = keyberon::layout::layout! { +/// pub static LAYERS: keyberon::layout::LayersArray<3, 1, 1> = keyberon::layout::layout! { /// { [ A B C ] } /// }; /// diff --git a/src/layout.rs b/src/layout.rs index 7e59c5a..5523ec3 100644 --- a/src/layout.rs +++ b/src/layout.rs @@ -25,10 +25,10 @@ /// Example layout for a 12x4 split keyboard: /// ``` /// use keyberon::action::Action; -/// use keyberon::layout::Layers; +/// use keyberon::layout::LayersArray; /// static DLAYER: Action = Action::DefaultLayer(5); /// -/// pub static LAYERS: Layers<12, 4, 2> = keyberon::layout::layout! { +/// pub static LAYERS: LayersArray<12, 4, 2> = keyberon::layout::layout! { /// { /// [ Tab Q W E R T Y U I O P BSpace ] /// [ LCtrl A S D F G H J K L ; Quote ] @@ -68,7 +68,6 @@ use State::*; /// Events can be retrieved by iterating over this struct and calling [Stacked::event]. type Stack = ArrayDeque<[Stacked; 16], arraydeque::behavior::Wrapping>; - /// The Layers trait. /// /// `Layers` conceptually is an array of layers which contain the description @@ -86,6 +85,12 @@ pub trait Layers { fn n_layers(&self) -> usize; } +/// Alias for defining layers as an array. +pub type LayersArray = [[[Action; C]; R]; L]; + +/// Alias for defining layers as a slice. +pub type LayersSlice<'a, T = core::convert::Infallible> = &'a [&'a [&'a [Action]]]; + impl Layers for [[[Action; C]; R]; L] { type CustomAction = T; @@ -100,7 +105,7 @@ impl Layers for [[[Action; } } -impl Layers for [&[&[Action]]] { +impl Layers for &[&[&[Action]]] { type CustomAction = T; fn get_action(&self, layer: usize, row: u8, col: u8) -> Option<&Action> { @@ -585,9 +590,63 @@ mod test { assert_eq!(expected, tested); } + #[test] + fn layers_as_array() { + static LAYERS: LayersArray<2, 1, 2> = [ + [[ + HoldTap { + timeout: 200, + hold: &l(1), + tap: &k(Space), + config: HoldTapConfig::Default, + tap_hold_interval: 0, + }, + HoldTap { + timeout: 200, + hold: &k(LCtrl), + tap: &k(Enter), + config: HoldTapConfig::Default, + tap_hold_interval: 0, + }, + ]], + [[Trans, m(&[LCtrl, Enter])]], + ]; + assert_eq!(LAYERS.n_layers(), 2); + assert_eq!(LAYERS.get_action(1, 0, 0), Some(&Trans)); + let mut layout = Layout::new(&LAYERS); + assert_eq!(CustomEvent::NoEvent, layout.tick()); + } + + #[test] + fn layers_as_slice() { + static LAYERS: LayersSlice = &[ + &[&[ + HoldTap { + timeout: 200, + hold: &l(1), + tap: &k(Space), + config: HoldTapConfig::Default, + tap_hold_interval: 0, + }, + HoldTap { + timeout: 200, + hold: &k(LCtrl), + tap: &k(Enter), + config: HoldTapConfig::Default, + tap_hold_interval: 0, + }, + ]], + &[&[Trans, m(&[LCtrl, Enter])]], + ]; + assert_eq!(LAYERS.n_layers(), 2); + assert_eq!(LAYERS.get_action(1, 0, 0), Some(&Trans)); + let mut layout = Layout::new(&LAYERS); + assert_eq!(CustomEvent::NoEvent, layout.tick()); + } + #[test] fn basic_hold_tap() { - static LAYERS: Layers<2, 1, 2> = [ + static LAYERS: LayersArray<2, 1, 2> = [ [[ HoldTap { timeout: 200, @@ -637,7 +696,7 @@ mod test { #[test] fn hold_tap_interleaved_timeout() { - static LAYERS: Layers<2, 1, 1> = [[[ + static LAYERS: LayersArray<2, 1, 1> = [[[ HoldTap { timeout: 200, hold: &k(LAlt), @@ -684,7 +743,7 @@ mod test { #[test] fn hold_on_press() { - static LAYERS: Layers<2, 1, 1> = [[[ + static LAYERS: LayersArray<2, 1, 1> = [[[ HoldTap { timeout: 200, hold: &k(LAlt), @@ -741,7 +800,7 @@ mod test { #[test] fn permissive_hold() { - static LAYERS: Layers<2, 1, 1> = [[[ + static LAYERS: LayersArray<2, 1, 1> = [[[ HoldTap { timeout: 200, hold: &k(LAlt), @@ -780,7 +839,7 @@ mod test { #[test] fn multiple_actions() { - static LAYERS: Layers<2, 1, 2> = [ + static LAYERS: LayersArray<2, 1, 2> = [ [[MultipleActions(&[l(1), k(LShift)]), k(F)]], [[Trans, k(E)]], ]; @@ -803,7 +862,7 @@ mod test { #[test] fn custom() { - static LAYERS: Layers<1, 1, 1, u8> = [[[Action::Custom(42)]]]; + static LAYERS: LayersArray<1, 1, 1, u8> = [[[Action::Custom(42)]]]; let mut layout = Layout::new(&LAYERS); assert_eq!(CustomEvent::NoEvent, layout.tick()); assert_keys(&[], layout.keycodes()); @@ -825,7 +884,7 @@ mod test { #[test] fn multiple_layers() { - static LAYERS: Layers<2, 1, 4> = [ + static LAYERS: LayersArray<2, 1, 4> = [ [[l(1), l(2)]], [[k(A), l(3)]], [[l(0), k(B)]], @@ -900,7 +959,7 @@ mod test { fn always_none(_: StackedIter) -> Option { None } - static LAYERS: Layers<4, 1, 1> = [[[ + static LAYERS: LayersArray<4, 1, 1> = [[[ HoldTap { timeout: 200, hold: &k(Kb1), @@ -993,7 +1052,7 @@ mod test { #[test] fn tap_hold_interval() { - static LAYERS: Layers<2, 1, 1> = [[[ + static LAYERS: LayersArray<2, 1, 1> = [[[ HoldTap { timeout: 200, hold: &k(LAlt), @@ -1047,7 +1106,7 @@ mod test { #[test] fn tap_hold_interval_interleave() { - static LAYERS: Layers<3, 1, 1> = [[[ + static LAYERS: LayersArray<3, 1, 1> = [[[ HoldTap { timeout: 200, hold: &k(LAlt), @@ -1168,7 +1227,7 @@ mod test { #[test] fn tap_hold_interval_short_hold() { - static LAYERS: Layers<1, 1, 1> = [[[HoldTap { + static LAYERS: LayersArray<1, 1, 1> = [[[HoldTap { timeout: 50, hold: &k(LAlt), tap: &k(Space), @@ -1209,7 +1268,7 @@ mod test { #[test] fn tap_hold_interval_different_hold() { - static LAYERS: Layers<2, 1, 1> = [[[ + static LAYERS: LayersArray<2, 1, 1> = [[[ HoldTap { timeout: 50, hold: &k(LAlt), diff --git a/src/lib.rs b/src/lib.rs index d04e8b2..d357e85 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -15,6 +15,10 @@ #![no_std] #![deny(missing_docs)] +#[cfg(test)] +#[macro_use] +extern crate alloc; + use usb_device::bus::UsbBusAllocator; use usb_device::prelude::*;