-
Notifications
You must be signed in to change notification settings - Fork 79
/
chording.rs
302 lines (278 loc) · 10.9 KB
/
chording.rs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
//! Chording implemention to mimic a single key.
//!
//! Provides chord support for emulating a single layout event
//! from multiple key presses. The single event press is triggered
//! once all the keys of the chord have been pressed and the chord
//! is released once all of the keys of the chord have been released.
//!
//! The chording tick should be used after debouncing, where
//! the debounce period determines the period in which all keys
//! need to be pressed to trigger the chord.
//!
//! You must use a virtual row/area of your layout to
//! define the result of the chord if the desired result is
//! not already on the layer that you want to use the chord on.
/// ## Usage
/// ```
/// use keyberon::chording::{Chording, ChordDef};
/// use keyberon::layout::{Layout, Event::*, Event};
/// use keyberon::debounce::Debouncer;
/// use keyberon::matrix::Matrix;
///
/// // The chord is defined by two or more locations in the layout
/// // that correspond to a single location in the layout
/// const CHORDS: [ChordDef; 2] = [
/// ((0, 2), &[(0, 0), (0, 1)]),
/// ((0, 0), &[(0, 1), (0, 2)]),
/// ];
/// // 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! {
/// { [ A B C ] }
/// };
///
/// let mut layout = Layout::new(&LAYERS);
/// // Debouncer period determines chording timeout
/// let mut debouncer: Debouncer<[[bool; 3]; 1]> =
/// Debouncer::new([[false; 3]; 1], [[false; 3]; 1], DEBOUNCE_COUNT);
/// let mut chording = Chording::new(&CHORDS);
///
/// // the rest of this example should be called inside a callback
/// // The PressedKeys are normally determined by calling the matrix
/// // and the for loop is just to get past the debouncer
/// for _ in 0..DEBOUNCE_COUNT {
/// assert_eq!(0, debouncer.events([[true, true, false]]).count());
/// }
/// let mut events = chording
/// .tick(debouncer.events([[true, true, false]]).collect())
/// .into_iter();
/// let event = events.next();
/// assert_eq!(Some(Event::Press(0, 2)), event);
/// layout.event(event.unwrap());
/// let event = events.next();
/// assert_eq!(None, event);
/// ```
use crate::layout::Event;
use heapless::Vec;
type KeyPosition = (u8, u8);
/// Description of the virtual key corresponding to a given chord.
/// keys are the coordinates of the multiple keys that make up the chord
/// result is the outcome of the keys being pressed
pub type ChordDef = (KeyPosition, &'static [KeyPosition]);
/// Runtime data for a chord
#[derive(Clone)]
struct Chord {
def: &'static ChordDef,
in_progress: bool,
keys_pressed: Vec<bool, 8>,
}
impl Chord {
/// Create new chord from user data.
fn new(def: &'static ChordDef) -> Self {
let mut me = Self {
def,
in_progress: false,
keys_pressed: Vec::new(),
};
for _ in def.1 {
me.keys_pressed.push(false).unwrap()
}
me
}
fn tick(&mut self, events: &[Event]) {
for e in events {
for (k, _) in self
.def
.1
.iter()
.enumerate()
.filter(|(_, key)| **key == e.coord())
{
self.keys_pressed[k] = e.is_press();
}
}
}
fn contains_chord(&mut self, events: &[Event]) -> bool {
for key in self.def.1 {
if !events.iter().any(|&k| (&k.coord() == key && k.is_press())) {
return false;
}
}
true
}
fn handle_chord(&mut self, events: &mut Vec<Event, 8>) {
self.in_progress = true;
for key in self.def.1 {
if let Some(position) = events
.iter()
.position(|&k| (&k.coord() == key && k.is_press()))
{
events.swap_remove(position);
}
}
events
.push(Event::Press(self.def.0 .0, self.def.0 .1))
.unwrap();
}
fn handle_release(&mut self, events: &mut Vec<Event, 8>) {
if self.in_progress {
for key in self.def.1 {
if let Some(position) = events
.iter()
.position(|&k| (&k.coord() == key && k.is_release()))
{
events.swap_remove(position);
}
}
if self.keys_pressed.iter().all(|&k| !k) {
events
.push(Event::Release(self.def.0 .0, self.def.0 .1))
.unwrap();
self.in_progress = false;
}
}
}
}
/// The chording manager. Initialize with a list of chord
/// definitions, and update after debounce
pub struct Chording<const N: usize> {
/// Defined chords
chords: Vec<Chord, N>,
}
impl<const N: usize> Chording<N> {
/// Take the predefined chord list in.
pub fn new(chords: &'static [ChordDef; N]) -> Self {
Self {
chords: chords.iter().map(Chord::new).collect(),
}
}
/// Consolidate events and return processed results as a result.
pub fn tick(&mut self, mut vec: Vec<Event, 8>) -> Vec<Event, 8> {
for c in &mut self.chords {
c.tick(&vec);
if c.contains_chord(&vec) {
c.handle_chord(&mut vec);
}
c.handle_release(&mut vec);
}
vec
}
}
#[cfg(test)]
mod test {
use super::{ChordDef, Chording};
use crate::layout::{Event, Event::*};
use heapless::Vec;
#[test]
fn single_press_release() {
const CHORDS: [ChordDef; 1] = [((0, 2), &[(0, 0), (0, 1)])];
let mut chording = Chording::new(&CHORDS);
// Verify a single press goes through chording unchanged
let mut single_press = Vec::<Event, 8>::new();
single_press.push(Press(0, 0)).ok();
assert_eq!(chording.tick(single_press), &[Press(0, 0)]);
let mut single_release = Vec::<Event, 8>::new();
single_release.push(Release(0, 0)).ok();
assert_eq!(chording.tick(single_release), &[Release(0, 0)]);
}
#[test]
fn chord_press_release() {
const CHORDS: [ChordDef; 1] = [((0, 2), &[(0, 0), (0, 1)])];
let mut chording = Chording::new(&CHORDS);
// Verify a chord is converted to the correct key
let mut double_press = Vec::<Event, 8>::new();
double_press.push(Press(0, 0)).ok();
double_press.push(Press(0, 1)).ok();
assert_eq!(chording.tick(double_press), &[Press(0, 2)]);
let nothing = Vec::<Event, 8>::new();
assert_eq!(chording.tick(nothing), &[]);
let mut double_release = Vec::<Event, 8>::new();
double_release.push(Release(0, 0)).ok();
double_release.push(Release(0, 1)).ok();
let chord_double_release = chording.tick(double_release);
assert_eq!(chord_double_release, &[Release(0, 2)]);
let nothing = Vec::<Event, 8>::new();
assert_eq!(chording.tick(nothing), &[]);
}
#[test]
fn chord_individual_press() {
const CHORDS: [ChordDef; 1] = [((0, 2), &[(0, 0), (0, 1)])];
let mut chording = Chording::new(&CHORDS);
// Verify that pressing the keys that make up a chord at different
// times will not trigger the chord
let mut key_a_press = Vec::<Event, 8>::new();
key_a_press.push(Press(0, 0)).ok();
assert_eq!(chording.tick(key_a_press), &[Press(0, 0)]);
let mut key_b_press = Vec::<Event, 8>::new();
key_b_press.push(Press(0, 1)).ok();
assert_eq!(chording.tick(key_b_press), &[Press(0, 1)]);
let nothing = Vec::<Event, 8>::new();
assert_eq!(chording.tick(nothing), &[]);
}
#[test]
fn chord_press_half_release() {
const CHORDS: [ChordDef; 1] = [((0, 2), &[(0, 0), (0, 1)])];
let mut chording = Chording::new(&CHORDS);
// Verify a chord is converted to the correct key
let mut double_press = Vec::<Event, 8>::new();
double_press.push(Press(0, 0)).ok();
double_press.push(Press(0, 1)).ok();
assert_eq!(chording.tick(double_press), &[Press(0, 2)]);
let mut first_release = Vec::<Event, 8>::new();
first_release.push(Release(0, 0)).ok();
// we don't want to see the release pass through of a single key
assert_eq!(chording.tick(first_release), &[]);
let mut second_release = Vec::<Event, 8>::new();
second_release.push(Release(0, 1)).ok();
// once all keys of the combo are released, the combo is released
assert_eq!(chording.tick(second_release), &[Release(0, 2)]);
}
#[test]
fn chord_overlap_press_release() {
const CHORDS: [ChordDef; 3] = [
((1, 0), &[(0, 0), (0, 1), (0, 2)]),
((1, 1), &[(0, 0), (0, 1)]),
((1, 2), &[(0, 1), (0, 2)]),
];
let mut chording = Chording::new(&CHORDS);
// Triple press chord is composed of the two keys that make their
// own unique chord. Only the three key chord should be triggered
let mut triple_press = Vec::<Event, 8>::new();
triple_press.push(Press(0, 0)).ok();
triple_press.push(Press(0, 1)).ok();
triple_press.push(Press(0, 2)).ok();
assert_eq!(chording.tick(triple_press), &[Press(1, 0)]);
let mut triple_release = Vec::<Event, 8>::new();
triple_release.push(Release(0, 0)).ok();
triple_release.push(Release(0, 1)).ok();
triple_release.push(Release(0, 2)).ok();
assert_eq!(chording.tick(triple_release), &[Release(1, 0)]);
// Verifying that the double key chord is pressed and released and not
// stalled by the overlapping three key chord
let mut double_press = Vec::<Event, 8>::new();
double_press.push(Press(0, 0)).ok();
double_press.push(Press(0, 1)).ok();
assert_eq!(chording.tick(double_press), &[Press(1, 1)]);
let mut double_release = Vec::<Event, 8>::new();
double_release.push(Release(0, 0)).ok();
double_release.push(Release(0, 1)).ok();
assert_eq!(chording.tick(double_release), &[Release(1, 1)]);
// If a three key chord has not been fully released, the released keys
// that form another chord should still work to press and release the
// two key chord
let mut triple_press = Vec::<Event, 8>::new();
triple_press.push(Press(0, 0)).ok();
triple_press.push(Press(0, 1)).ok();
triple_press.push(Press(0, 2)).ok();
assert_eq!(chording.tick(triple_press), &[Press(1, 0)]);
let mut half_triple_release = Vec::<Event, 8>::new();
half_triple_release.push(Release(0, 1)).ok();
half_triple_release.push(Release(0, 2)).ok();
assert_eq!(chording.tick(half_triple_release), &[]);
let mut double_press = Vec::<Event, 8>::new();
double_press.push(Press(0, 1)).ok();
double_press.push(Press(0, 2)).ok();
assert_eq!(chording.tick(double_press), &[Press(1, 2)]);
}
}