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
303
304
305
306
//! The different actions that can be executed via any given key.
use crate::key_code::KeyCode;
use crate::layout::{StackedIter, WaitingAction};
use core::fmt::Debug;
/// The different types of actions we support for key sequences/macros
#[non_exhaustive]
#[derive(Debug, Clone, Copy, Eq, PartialEq)]
pub enum SequenceEvent {
/// No operation action: just do nothing (a placeholder).
NoOp,
/// A keypress/keydown
Press(KeyCode),
/// Key release/keyup
Release(KeyCode),
/// A shortcut for `Press(KeyCode), Release(KeyCode)`
Tap(KeyCode),
/// For sequences that need to wait a bit before continuing
Delay {
/// How long (in ticks) this Delay will last
duration: u32, // NOTE: This isn't a u16 because that's only max ~65 seconds (assuming 1000 ticks/sec)
},
/// A marker that indicates there's more of the macro than would fit
/// in the 'sequenced' ArrayDeque
Continue {
/// The current chunk
index: usize,
/// The full list of Sequence Events (that aren't Continue())
events: &'static [SequenceEvent],
},
/// Cancels the running sequence and can be used to mark the end of a sequence
/// instead of using a number of Release() events
Complete,
}
/// Behavior configuration of HoldTap.
#[non_exhaustive]
#[derive(Clone, Copy)]
pub enum HoldTapConfig {
/// Only the timeout will determine between hold and tap action.
///
/// This is a sane default.
Default,
/// If there is a key press, the hold action is activated.
///
/// This behavior is interesting for a key which the tap action is
/// not used in the flow of typing, like escape for example. If
/// you are annoyed by accidental tap, you can try this behavior.
HoldOnOtherKeyPress,
/// If there is a press and release of another key, the hold
/// action is activated.
///
/// This behavior is interesting for fast typist: the different
/// between hold and tap would more be based on the sequence of
/// events than on timing. Be aware that doing the good succession
/// of key might require some training.
PermissiveHold,
/// A custom configuration. Allows the behavior to be controlled by a caller
/// supplied handler function.
///
/// The input to the custom handler will be an iterator that returns
/// [Stacked] [Events](Event). The order of the events matches the order the
/// corresponding key was pressed/released, i.e. the first event is the
/// event first received after the HoldTap action key is pressed.
///
/// The return value should be the intended action that should be used. A
/// [Some] value will cause one of: [WaitingAction::Tap] for the configured
/// tap action, [WaitingAction::Hold] for the hold action, and
/// [WaitingAction::NoOp] to drop handling of the key press. A [None]
/// value will cause a fallback to the timeout-based approach. If the
/// timeout is not triggered, the next tick will call the custom handler
/// again.
///
/// # Example:
/// Hold events can be prevented from triggering when pressing multiple keys
/// on the same side of the keyboard (but does not prevent multiple hold
/// events).
/// ```
/// use kanata_keyberon::action::{Action, HoldTapConfig};
/// use kanata_keyberon::key_code::KeyCode;
/// use kanata_keyberon::layout::{StackedIter, WaitingAction, Event};
///
/// /// Trigger a `Tap` action on the left side of the keyboard if another
/// /// key on the left side of the keyboard is pressed.
/// fn left_mod(stacked_iter: StackedIter) -> Option<WaitingAction> {
/// match stacked_iter.map(|s| s.event()).find(|e| e.is_press()) {
/// Some(Event::Press(_, j)) if j < 6 => Some(WaitingAction::Tap),
/// _ => None,
/// }
/// }
///
/// /// Trigger a `Tap` action on the right side of the keyboard if another
/// /// key on the right side of the keyboard is pressed.
/// fn right_mod(stacked_iter: StackedIter) -> Option<WaitingAction> {
/// match stacked_iter.map(|s| s.event()).find(|e| e.is_press()) {
/// Some(Event::Press(_, j)) if j > 5 => Some(WaitingAction::Tap),
/// _ => None,
/// }
/// }
///
/// // Assuming a standard QWERTY layout, the left shift hold action will
/// // not be triggered when pressing Tab-T, CapsLock-G, nor Shift-B.
/// const A_SHIFT: Action = Action::HoldTap {
/// timeout: 200,
/// hold: &Action::KeyCode(KeyCode::LShift),
/// tap: &Action::KeyCode(KeyCode::A),
/// config: HoldTapConfig::Custom(left_mod),
/// tap_hold_interval: 0,
/// };
///
/// // Assuming a standard QWERTY layout, the right shift hold action will
/// // not be triggered when pressing Y-Pipe, H-Enter, nor N-Shift.
/// const SEMI_SHIFT: Action = Action::HoldTap {
/// timeout: 200,
/// hold: &Action::KeyCode(KeyCode::RShift),
/// tap: &Action::KeyCode(KeyCode::SColon),
/// config: HoldTapConfig::Custom(right_mod),
/// tap_hold_interval: 0,
/// };
/// ```
Custom(fn(StackedIter) -> Option<WaitingAction>),
}
impl Debug for HoldTapConfig {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
match self {
HoldTapConfig::Default => f.write_str("Default"),
HoldTapConfig::HoldOnOtherKeyPress => f.write_str("HoldOnOtherKeyPress"),
HoldTapConfig::PermissiveHold => f.write_str("PermissiveHold"),
HoldTapConfig::Custom(func) => f
.debug_tuple("Custom")
.field(&(*func as fn(StackedIter<'static>) -> Option<WaitingAction>) as &dyn Debug)
.finish(),
}
}
}
impl PartialEq for HoldTapConfig {
fn eq(&self, other: &Self) -> bool {
match (self, other) {
(HoldTapConfig::Default, HoldTapConfig::Default)
| (HoldTapConfig::HoldOnOtherKeyPress, HoldTapConfig::HoldOnOtherKeyPress)
| (HoldTapConfig::PermissiveHold, HoldTapConfig::PermissiveHold) => true,
(HoldTapConfig::Custom(self_func), HoldTapConfig::Custom(other_func)) => {
*self_func as fn(StackedIter<'static>) -> Option<WaitingAction> == *other_func
}
_ => false,
}
}
}
impl Eq for HoldTapConfig {}
/// The different actions that can be done.
#[non_exhaustive]
#[derive(Debug, Clone, Copy, Eq, PartialEq)]
pub enum Action<T = core::convert::Infallible>
where
T: 'static,
{
/// No operation action: just do nothing.
NoOp,
/// Transparent, i.e. get the action from the default layer. On
/// the default layer, it is equivalent to `NoOp`.
Trans,
/// A key code, i.e. a classic key.
KeyCode(KeyCode),
/// Multiple key codes sent at the same time, as if these keys
/// were pressed at the same time. Useful to send a shifted key,
/// or complex shortcuts like Ctrl+Alt+Del in a single key press.
MultipleKeyCodes(&'static [KeyCode]),
/// Multiple actions sent at the same time.
MultipleActions(&'static [Action<T>]),
/// While pressed, change the current layer. That's the classic
/// Fn key. If several layer actions are hold at the same time,
/// the last pressed defines the current layer.
Layer(usize),
/// Change the default layer.
DefaultLayer(usize),
/// If the key is held more than `timeout` ticks (usually
/// milliseconds), performs the `hold` action, else performs the
/// `tap` action. Mostly used with a modifier for the hold action
/// and a normal key on the tap action. Any action can be
/// performed, but using a `HoldTap` in a `HoldTap` is not
/// specified (but guaranteed to not crash).
///
/// Different behaviors can be configured using the config field,
/// but whatever the configuration is, if the key is pressed more
/// than `timeout`, the hold action is activated (if no other
/// action was determined before).
HoldTap {
/// The duration, in ticks (usually milliseconds) giving the
/// difference between a hold and a tap.
timeout: u16,
/// The hold action.
hold: &'static Action<T>,
/// The tap action.
tap: &'static Action<T>,
/// Behavior configuration.
config: HoldTapConfig,
/// Configuration of the tap and hold holds the tap action.
///
/// If you press and release the key in such a way that the tap
/// action is performed, and then press it again in less than
/// `tap_hold_interval` ticks, the tap action will
/// be held. This allows the tap action to be held by
/// pressing, releasing and holding the key, allowing the computer
/// to auto repeat the tap behavior. The timeout starts on the
/// first press of the key, NOT on the release.
///
/// Pressing a different key in between will not result in the
/// behaviour described above; the HoldTap key must be pressed twice
/// in a row.
///
/// To deactivate the functionality, set this to 0.
tap_hold_interval: u16,
},
/// A sequence of SequenceEvents
Sequence {
/// An array of SequenceEvents that will be triggered (in order)
events: &'static [SequenceEvent],
},
/// Cancels any running sequences
CancelSequences,
/// One-shot key. Activates `action` until a single other key is pressed following the
/// one shot activation or `timeout` ticks elapse. For example, a one-shot key can be
/// used to activate shift for exactly one keypress or switch to another layer for
/// exactly one keypress.
///
/// Holding a one-shot key will be treated as a normal held keypress.
///
/// If you use one-shot outside of its intended use cases (modifier key action or layer
/// action) then you will likely have undesired behaviour. E.g. one-shot with the space
/// key will hold space until either another key is pressed or the timeout occurs, which will
/// probably send many undesired space characters to your active application.
OneShot {
/// Action to activate until timeout expires or exactly one keypress is activated.
action: &'static Action<T>,
/// Timeout after which one-shot will expire.
timeout: u16,
},
/// Tap-dance key. When tapping the key N times in quck succession, activates the N'th action
/// in `actions`. The action will activate in the following conditions:
///
/// - a different key is pressed
/// - `timeout` ticks elapse since the last tap of the same tap-dance key
/// - the number of taps is equal to the length of `actions`.
TapDance {
/// List of actions that activate based on number of taps. Only one of the actions will
/// activate. Tapping the tap-dance key once will activate the action in index 0, three
/// times will activate the action in index 2.
actions: &'static [&'static Action<T>],
/// Timeout after which a tap will expire and become an action. A new tap for the same
/// tap-dance key will reset this timeout.
timeout: u16,
},
/// Custom action.
///
/// Define a user defined action. This enum can be anything you
/// want, as long as it has the `'static` lifetime. It can be used
/// to drive any non keyboard related actions that you might
/// manage with key events.
Custom(T),
}
impl<T> Action<T> {
/// Gets the layer number if the action is the `Layer` action.
pub fn layer(self) -> Option<usize> {
match self {
Action::Layer(l) => Some(l),
_ => None,
}
}
/// Returns an iterator on the `KeyCode` corresponding to the action.
pub fn key_codes(&self) -> impl Iterator<Item = KeyCode> + '_ {
match self {
Action::KeyCode(kc) => core::slice::from_ref(kc).iter().cloned(),
Action::MultipleKeyCodes(kcs) => kcs.iter().cloned(),
_ => [].iter().cloned(),
}
}
}
/// A shortcut to create a `Action::KeyCode`, useful to create compact
/// layout.
pub const fn k<T>(kc: KeyCode) -> Action<T> {
Action::KeyCode(kc)
}
/// A shortcut to create a `Action::Layer`, useful to create compact
/// layout.
pub const fn l<T>(layer: usize) -> Action<T> {
Action::Layer(layer)
}
/// A shortcut to create a `Action::DefaultLayer`, useful to create compact
/// layout.
pub const fn d<T>(layer: usize) -> Action<T> {
Action::DefaultLayer(layer)
}
/// A shortcut to create a `Action::MultipleKeyCodes`, useful to
/// create compact layout.
pub const fn m<T>(kcs: &'static [KeyCode]) -> Action<T> {
Action::MultipleKeyCodes(kcs)
}