Skip to content

Commit 848ece0

Browse files
committed
new taphold implement, buffering and state
* refactory tap build from pooling event channel to buffer vec * fix bug for multiple hold key introduce new featues to timeless HRM * permissive hold: for same hand hold while release following keys * chordal hold: hold on presss corss hand TODO deprecate and remove post wait timeout
1 parent 3d68f03 commit 848ece0

File tree

11 files changed

+2178
-383
lines changed

11 files changed

+2178
-383
lines changed

rmk-config/src/lib.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -292,6 +292,8 @@ pub struct BehaviorConfig {
292292
#[serde(deny_unknown_fields)]
293293
pub struct TapHoldConfig {
294294
pub enable_hrm: Option<bool>,
295+
pub permissive_hold: Option<bool>,
296+
pub chordal_hold: Option<bool>,
295297
pub prior_idle_time: Option<DurationMillis>,
296298
pub post_wait_time: Option<DurationMillis>,
297299
pub hold_timeout: Option<DurationMillis>,

rmk-macro/src/behavior.rs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,14 @@ fn expand_tap_hold(tap_hold: &Option<TapHoldConfig>) -> proc_macro2::TokenStream
4848
Some(enable) => quote! { enable_hrm: #enable, },
4949
None => quote! {},
5050
};
51+
let permissive_hold = match tap_hold.permissive_hold{
52+
Some(enable) => quote! { permissive_hold: #enable, },
53+
None => quote! {},
54+
};
55+
let chordal_hold = match tap_hold.chordal_hold{
56+
Some(enable) => quote! { chordal_hold: #enable, },
57+
None => quote! {},
58+
};
5159
let prior_idle_time = match &tap_hold.prior_idle_time {
5260
Some(t) => {
5361
let timeout = t.0;
@@ -76,6 +84,8 @@ fn expand_tap_hold(tap_hold: &Option<TapHoldConfig>) -> proc_macro2::TokenStream
7684
#prior_idle_time
7785
#post_wait_time
7886
#hold_timeout
87+
#permissive_hold
88+
#chordal_hold
7989
..Default::default()
8090
}
8191
}

rmk/src/config/mod.rs

Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,10 +12,12 @@ use heapless::Vec;
1212
use macro_config::KeyboardMacrosConfig;
1313
#[cfg(feature = "_nrf_ble")]
1414
pub use nrf_config::BleBatteryConfig;
15+
use num_enum::FromPrimitive;
1516

1617
use crate::combo::Combo;
1718
use crate::fork::Fork;
1819
use crate::{COMBO_MAX_NUM, FORK_MAX_NUM};
20+
use crate::event::KeyEvent;
1921

2022
/// The config struct for RMK keyboard.
2123
///
@@ -82,15 +84,54 @@ pub struct TapHoldConfig {
8284
pub prior_idle_time: Duration,
8385
pub post_wait_time: Duration,
8486
pub hold_timeout: Duration,
87+
pub permissive_hold: bool,
88+
pub chordal_hold: bool,
89+
}
90+
91+
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)]
92+
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
93+
pub enum ChordHoldHand {
94+
Left,
95+
Right,
96+
}
97+
98+
#[derive(Clone, Debug)]
99+
pub struct ChordHoldState<const COLS: usize> {
100+
pub hand: ChordHoldHand,
101+
}
102+
103+
impl<const COLS: usize> ChordHoldState<COLS> {
104+
// is the key event in the same side of current chord hold
105+
pub fn is_same(&self, key_event: KeyEvent) -> bool {
106+
match self.hand {
107+
ChordHoldHand::Left => (key_event.col as usize) < COLS / 2,
108+
ChordHoldHand::Right => (key_event.col as usize) >= COLS / 2,
109+
}
110+
}
111+
112+
pub(crate) fn create(event: KeyEvent, cols: usize) -> Self {
113+
if (event.col as usize) < (cols / 2) {
114+
ChordHoldState {
115+
hand: ChordHoldHand::Left,
116+
}
117+
} else {
118+
ChordHoldState {
119+
hand: ChordHoldHand::Right,
120+
}
121+
}
122+
}
85123
}
86124

87125
impl Default for TapHoldConfig {
88126
fn default() -> Self {
89127
Self {
90128
enable_hrm: false,
129+
permissive_hold: false,
130+
91131
prior_idle_time: Duration::from_millis(120),
92132
post_wait_time: Duration::from_millis(50),
93133
hold_timeout: Duration::from_millis(250),
134+
chordal_hold: false,
94135
}
95136
}
96137
}
@@ -224,3 +265,66 @@ impl Default for KeyboardUsbConfig<'_> {
224265
}
225266
}
226267
}
268+
269+
mod tests {
270+
use super::*;
271+
use ChordHoldHand::Left;
272+
use ChordHoldHand::Right;
273+
274+
#[test]
275+
fn test_chord_hold() {
276+
assert_eq!(
277+
ChordHoldState::<6>::create(
278+
KeyEvent {
279+
row: 0,
280+
col: 0,
281+
pressed: true,
282+
},
283+
6
284+
)
285+
.hand,
286+
Left
287+
);
288+
assert_eq!(
289+
ChordHoldState::<6>::create(
290+
KeyEvent {
291+
row: 3,
292+
col: 3,
293+
pressed: true,
294+
},
295+
6
296+
)
297+
.hand,
298+
Right
299+
);
300+
assert_eq!(
301+
ChordHoldState::<6>::create(
302+
KeyEvent {
303+
row: 5,
304+
col: 6,
305+
pressed: true,
306+
},
307+
6
308+
)
309+
.hand,
310+
Right
311+
);
312+
313+
let chord = ChordHoldState::<6> { hand: Left };
314+
315+
let vec: Vec<_, 6> = Vec::from_slice(&[0u8, 1, 2, 3, 4, 5]).unwrap();
316+
let result: Vec<_, 6> = vec
317+
.iter()
318+
.map(|col| {
319+
chord.is_same(KeyEvent {
320+
row: 0,
321+
col: 0 + col,
322+
pressed: true,
323+
})
324+
})
325+
.collect();
326+
327+
let result2: Vec<bool, 6> = Vec::from_slice(&[true, true, true, false, false, false]).unwrap();
328+
assert_eq!(result, result2);
329+
}
330+
}

rmk/src/event.rs

Lines changed: 145 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,10 @@
1+
use embassy_time::Instant;
12
use postcard::experimental::max_size::MaxSize;
23
use serde::{Deserialize, Serialize};
34

45
use crate::{input_device::rotary_encoder::Direction, keycode::ModifierCombination};
6+
use crate::action::{KeyAction, Action};
7+
use crate::keycode::KeyCode::No;
58

69
/// Raw events from input devices and keyboards
710
///
@@ -118,3 +121,145 @@ pub enum ControllerEvent {
118121
/// Split peripheral connection
119122
SplitPeripheral(u8, bool),
120123
}
124+
125+
#[derive(Clone, Copy, Debug)]
126+
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
127+
pub enum HoldingKey {
128+
// tap hold key
129+
TapHold(PressedTapHold),
130+
// normal key
131+
Tapping(BufferedPressEvent),
132+
}
133+
134+
pub trait HoldingKeyTrait {
135+
fn update_state(&mut self, new_state: TapHoldState);
136+
fn press_time(&self) -> Instant;
137+
fn state(&self) -> TapHoldState;
138+
}
139+
140+
impl HoldingKey {
141+
pub(crate) fn start_time(&self) -> Instant {
142+
match self {
143+
HoldingKey::TapHold(h) => h.pressed_time,
144+
HoldingKey::Tapping(h) => h.pressed_time,
145+
}
146+
}
147+
148+
pub(crate) fn key_event(&self) -> KeyEvent {
149+
match self {
150+
HoldingKey::TapHold(h) => h.key_event,
151+
HoldingKey::Tapping(h) => h.key_event,
152+
}
153+
}
154+
155+
pub(crate) fn is_tap_hold(&self) -> bool {
156+
matches!(self, HoldingKey::TapHold(_))
157+
}
158+
}
159+
160+
impl HoldingKeyTrait for HoldingKey {
161+
fn update_state(&mut self, new_state: TapHoldState) {
162+
match self {
163+
HoldingKey::TapHold(h) => {
164+
h.state = new_state;
165+
}
166+
HoldingKey::Tapping(t) => {
167+
t.state = new_state;
168+
}
169+
}
170+
}
171+
172+
fn press_time(&self) -> Instant {
173+
match self {
174+
HoldingKey::TapHold(h) => h.pressed_time,
175+
HoldingKey::Tapping(t) => t.pressed_time,
176+
}
177+
}
178+
179+
fn state(&self) -> TapHoldState {
180+
match self {
181+
HoldingKey::TapHold(h) => h.state,
182+
HoldingKey::Tapping(t) => t.state,
183+
}
184+
}
185+
}
186+
187+
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
188+
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
189+
pub enum TapHoldState {
190+
//happen after press event arrives
191+
Initial,
192+
//tapping event
193+
Tap,
194+
//release tapping event
195+
PostTap,
196+
//holding event
197+
Hold,
198+
//release holding event
199+
PostHold,
200+
//reserved
201+
Release,
202+
}
203+
204+
// record pressing tap hold keys
205+
#[derive(Clone, Copy, Debug)]
206+
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
207+
pub struct PressedTapHold {
208+
pub key_event: KeyEvent,
209+
pub tap_action: Action,
210+
pub hold_action: Action,
211+
pub pressed_time: Instant,
212+
pub deadline: Instant,
213+
pub state: TapHoldState,
214+
}
215+
216+
impl PressedTapHold {
217+
const NO: Action = Action::Key(No);
218+
219+
pub(crate) fn hold_action(&self) -> Action {
220+
self.hold_action
221+
}
222+
223+
pub(crate) fn tap_action(&self) -> Action {
224+
self.tap_action
225+
}
226+
}
227+
228+
impl HoldingKeyTrait for PressedTapHold {
229+
fn update_state(&mut self, new_state: TapHoldState) {
230+
self.state = new_state
231+
}
232+
233+
fn press_time(&self) -> Instant {
234+
self.pressed_time
235+
}
236+
237+
fn state(&self) -> TapHoldState {
238+
self.state
239+
}
240+
}
241+
242+
//buffered pressing key event while TapHolding
243+
#[derive(Clone, Copy, Debug)]
244+
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
245+
pub struct BufferedPressEvent {
246+
pub key_event: KeyEvent,
247+
pub key_action: KeyAction,
248+
pub pressed_time: Instant,
249+
//initial -> tap -> post tap -> release
250+
pub state: TapHoldState,
251+
}
252+
253+
impl HoldingKeyTrait for BufferedPressEvent {
254+
fn update_state(&mut self, new_state: TapHoldState) {
255+
self.state = new_state
256+
}
257+
258+
fn press_time(&self) -> Instant {
259+
self.pressed_time
260+
}
261+
262+
fn state(&self) -> TapHoldState {
263+
self.state
264+
}
265+
}

0 commit comments

Comments
 (0)