aboutsummaryrefslogtreecommitdiff
path: root/quantum/process_keycode
diff options
context:
space:
mode:
Diffstat (limited to 'quantum/process_keycode')
-rw-r--r--quantum/process_keycode/process_combo.c558
-rw-r--r--quantum/process_keycode/process_combo.h38
-rw-r--r--quantum/process_keycode/process_haptic.c147
-rw-r--r--quantum/process_keycode/process_haptic.h21
-rw-r--r--quantum/process_keycode/process_key_override.c518
-rw-r--r--quantum/process_keycode/process_key_override.h153
-rw-r--r--quantum/process_keycode/process_magic.c3
-rw-r--r--quantum/process_keycode/process_music.c2
-rw-r--r--quantum/process_keycode/process_music.h2
-rw-r--r--quantum/process_keycode/process_rgb.c7
-rw-r--r--quantum/process_keycode/process_steno.c15
-rw-r--r--quantum/process_keycode/process_tap_dance.c2
-rw-r--r--quantum/process_keycode/process_tap_dance.h2
13 files changed, 1347 insertions, 121 deletions
diff --git a/quantum/process_keycode/process_combo.c b/quantum/process_keycode/process_combo.c
index f38d7d47a..e8661839c 100644
--- a/quantum/process_keycode/process_combo.c
+++ b/quantum/process_keycode/process_combo.c
@@ -16,114 +16,449 @@
16 16
17#include "print.h" 17#include "print.h"
18#include "process_combo.h" 18#include "process_combo.h"
19#include "action_tapping.h"
19 20
20#ifndef COMBO_VARIABLE_LEN 21
21__attribute__((weak)) combo_t key_combos[COMBO_COUNT] = {}; 22#ifdef COMBO_COUNT
23__attribute__((weak)) combo_t key_combos[COMBO_COUNT];
24uint16_t COMBO_LEN = COMBO_COUNT;
22#else 25#else
23extern combo_t key_combos[]; 26extern combo_t key_combos[];
24extern int COMBO_LEN; 27extern uint16_t COMBO_LEN;
25#endif 28#endif
26 29
27__attribute__((weak)) void process_combo_event(uint16_t combo_index, bool pressed) {} 30__attribute__((weak)) void process_combo_event(uint16_t combo_index, bool pressed) {}
28 31
29static uint16_t timer = 0; 32#ifdef COMBO_MUST_HOLD_PER_COMBO
30static uint16_t current_combo_index = 0; 33__attribute__((weak)) bool get_combo_must_hold(uint16_t index, combo_t *combo) { return false; }
31static bool drop_buffer = false; 34#endif
32static bool is_active = false; 35
33static bool b_combo_enable = true; // defaults to enabled 36#ifdef COMBO_MUST_TAP_PER_COMBO
37__attribute__((weak)) bool get_combo_must_tap(uint16_t index, combo_t *combo) { return false; }
38#endif
39
40#ifdef COMBO_TERM_PER_COMBO
41__attribute__((weak)) uint16_t get_combo_term(uint16_t index, combo_t *combo) { return COMBO_TERM; }
42#endif
43
44#ifdef COMBO_PROCESS_KEY_RELEASE
45__attribute__((weak)) bool process_combo_key_release(uint16_t combo_index, combo_t *combo, uint8_t key_index, uint16_t keycode) { return false; }
46#endif
34 47
35static uint8_t buffer_size = 0; 48#ifndef COMBO_NO_TIMER
36#ifdef COMBO_ALLOW_ACTION_KEYS 49static uint16_t timer = 0;
37static keyrecord_t key_buffer[MAX_COMBO_LENGTH]; 50#endif
51static bool b_combo_enable = true; // defaults to enabled
52static uint16_t longest_term = 0;
53
54typedef struct {
55 keyrecord_t record;
56 uint16_t combo_index;
57 uint16_t keycode;
58} queued_record_t;
59static uint8_t key_buffer_size = 0;
60static queued_record_t key_buffer[COMBO_KEY_BUFFER_LENGTH];
61
62typedef struct {
63 uint16_t combo_index;
64} queued_combo_t;
65static uint8_t combo_buffer_write= 0;
66static uint8_t combo_buffer_read = 0;
67static queued_combo_t combo_buffer[COMBO_BUFFER_LENGTH];
68
69#define INCREMENT_MOD(i) i = (i + 1) % COMBO_BUFFER_LENGTH
70
71#define COMBO_KEY_POS ((keypos_t){.col=254, .row=254})
72
73
74#ifndef EXTRA_SHORT_COMBOS
75/* flags are their own elements in combo_t struct. */
76# define COMBO_ACTIVE(combo) (combo->active)
77# define COMBO_DISABLED(combo) (combo->disabled)
78# define COMBO_STATE(combo) (combo->state)
79
80# define ACTIVATE_COMBO(combo) do {combo->active = true;}while(0)
81# define DEACTIVATE_COMBO(combo) do {combo->active = false;}while(0)
82# define DISABLE_COMBO(combo) do {combo->disabled = true;}while(0)
83# define RESET_COMBO_STATE(combo) do { \
84 combo->disabled = false; \
85 combo->state = 0; \
86}while(0)
38#else 87#else
39static uint16_t key_buffer[MAX_COMBO_LENGTH]; 88/* flags are at the two high bits of state. */
89# define COMBO_ACTIVE(combo) (combo->state & 0x80)
90# define COMBO_DISABLED(combo) (combo->state & 0x40)
91# define COMBO_STATE(combo) (combo->state & 0x3F)
92
93# define ACTIVATE_COMBO(combo) do {combo->state |= 0x80;}while(0)
94# define DEACTIVATE_COMBO(combo) do {combo->state &= ~0x80;}while(0)
95# define DISABLE_COMBO(combo) do {combo->state |= 0x40;}while(0)
96# define RESET_COMBO_STATE(combo) do {combo->state &= ~0x7F;}while(0)
40#endif 97#endif
41 98
42static inline void send_combo(uint16_t action, bool pressed) { 99static inline void release_combo(uint16_t combo_index, combo_t *combo) {
43 if (action) { 100 if (combo->keycode) {
44 if (pressed) { 101 keyrecord_t record = {
45 register_code16(action); 102 .event = {
46 } else { 103 .key = COMBO_KEY_POS,
47 unregister_code16(action); 104 .time = timer_read()|1,
48 } 105 .pressed = false,
106 },
107 .keycode = combo->keycode,
108 };
109#ifndef NO_ACTION_TAPPING
110 action_tapping_process(record);
111#else
112 process_record(&record);
113#endif
49 } else { 114 } else {
50 process_combo_event(current_combo_index, pressed); 115 process_combo_event(combo_index, false);
51 } 116 }
117 DEACTIVATE_COMBO(combo);
118}
119
120static inline bool _get_combo_must_hold(uint16_t combo_index, combo_t *combo) {
121#ifdef COMBO_NO_TIMER
122 return false;
123#elif defined(COMBO_MUST_HOLD_PER_COMBO)
124 return get_combo_must_hold(combo_index, combo);
125#elif defined(COMBO_MUST_HOLD_MODS)
126 return (KEYCODE_IS_MOD(combo->keycode) ||
127 (combo->keycode >= QK_MOMENTARY && combo->keycode <= QK_MOMENTARY_MAX));
128#endif
129 return false;
130}
131
132static inline uint16_t _get_wait_time(uint16_t combo_index, combo_t *combo ) {
133 if (_get_combo_must_hold(combo_index, combo)
134#ifdef COMBO_MUST_TAP_PER_COMBO
135 || get_combo_must_tap(combo_index, combo)
136#endif
137 ) {
138 if (longest_term < COMBO_HOLD_TERM) {
139 return COMBO_HOLD_TERM;
140 }
141 }
142
143 return longest_term;
144}
145
146static inline uint16_t _get_combo_term(uint16_t combo_index, combo_t *combo) {
147
148#if defined(COMBO_TERM_PER_COMBO)
149 return get_combo_term(combo_index, combo);
150#endif
151
152 return COMBO_TERM;
52} 153}
53 154
54static inline void dump_key_buffer(bool emit) { 155void clear_combos(void) {
55 if (buffer_size == 0) { 156 uint16_t index = 0;
157 longest_term = 0;
158 for (index = 0; index < COMBO_LEN; ++index) {
159 combo_t *combo = &key_combos[index];
160 if (!COMBO_ACTIVE(combo)) {
161 RESET_COMBO_STATE(combo);
162 }
163 }
164}
165
166static inline void dump_key_buffer(void) {
167 /* First call start from 0 index; recursive calls need to start from i+1 index */
168 static uint8_t key_buffer_next = 0;
169
170 if (key_buffer_size == 0) {
56 return; 171 return;
57 } 172 }
58 173
59 if (emit) { 174 for (uint8_t key_buffer_i = key_buffer_next; key_buffer_i < key_buffer_size; key_buffer_i++) {
60 for (uint8_t i = 0; i < buffer_size; i++) { 175 key_buffer_next = key_buffer_i + 1;
61#ifdef COMBO_ALLOW_ACTION_KEYS 176
62 const action_t action = store_or_get_action(key_buffer[i].event.pressed, key_buffer[i].event.key); 177 queued_record_t *qrecord = &key_buffer[key_buffer_i];
63 process_action(&(key_buffer[i]), action); 178 keyrecord_t *record = &qrecord->record;
179
180 if (IS_NOEVENT(record->event)) {
181 continue;
182 }
183
184 if (!record->keycode && qrecord->combo_index != (uint16_t)-1) {
185 process_combo_event(qrecord->combo_index, true);
186 } else {
187#ifndef NO_ACTION_TAPPING
188 action_tapping_process(*record);
64#else 189#else
65 register_code16(key_buffer[i]); 190 process_record(record);
66 send_keyboard_report();
67#endif 191#endif
68 } 192 }
193 record->event.time = 0;
69 } 194 }
70 195
71 buffer_size = 0; 196 key_buffer_next = key_buffer_size = 0;
72} 197}
73 198
74#define ALL_COMBO_KEYS_ARE_DOWN (((1 << count) - 1) == combo->state) 199#define NO_COMBO_KEYS_ARE_DOWN (0 == COMBO_STATE(combo))
75#define KEY_STATE_DOWN(key) \ 200#define ALL_COMBO_KEYS_ARE_DOWN(state, key_count) (((1 << key_count) - 1) == state)
76 do { \ 201#define ONLY_ONE_KEY_IS_DOWN(state) !(state & (state - 1))
77 combo->state |= (1 << key); \ 202#define KEY_NOT_YET_RELEASED(state, key_index) ((1 << key_index) & state)
203#define KEY_STATE_DOWN(state, key_index) \
204 do { \
205 state |= (1 << key_index); \
78 } while (0) 206 } while (0)
79#define KEY_STATE_UP(key) \ 207#define KEY_STATE_UP(state, key_index) \
80 do { \ 208 do { \
81 combo->state &= ~(1 << key); \ 209 state &= ~(1 << key_index); \
82 } while (0) 210 } while (0)
83 211
84static bool process_single_combo(combo_t *combo, uint16_t keycode, keyrecord_t *record) { 212static inline void _find_key_index_and_count(const uint16_t *keys, uint16_t keycode, uint16_t *key_index, uint8_t *key_count) {
85 uint8_t count = 0; 213 while (true) {
86 uint16_t index = -1; 214 uint16_t key = pgm_read_word(&keys[*key_count]);
87 /* Find index of keycode and number of combo keys */ 215 if (keycode == key) *key_index = *key_count;
88 for (const uint16_t *keys = combo->keys;; ++count) {
89 uint16_t key = pgm_read_word(&keys[count]);
90 if (keycode == key) index = count;
91 if (COMBO_END == key) break; 216 if (COMBO_END == key) break;
217 (*key_count)++;
218 }
219}
220
221void drop_combo_from_buffer(uint16_t combo_index) {
222 /* Mark a combo as processed from the buffer. If the buffer is in the
223 * beginning of the buffer, drop it. */
224 uint8_t i = combo_buffer_read;
225 while (i != combo_buffer_write) {
226 queued_combo_t *qcombo = &combo_buffer[i];
227
228 if (qcombo->combo_index == combo_index) {
229 combo_t *combo = &key_combos[combo_index];
230 DISABLE_COMBO(combo);
231
232 if (i == combo_buffer_read) {
233 INCREMENT_MOD(combo_buffer_read);
234 }
235 break;
236 }
237 INCREMENT_MOD(i);
92 } 238 }
239}
240
241void apply_combo(uint16_t combo_index, combo_t *combo) {
242 /* Apply combo's result keycode to the last chord key of the combo and
243 * disable the other keys. */
93 244
94 /* Continue processing if not a combo key */ 245 if (COMBO_DISABLED(combo)) { return; }
95 if (-1 == (int8_t)index) return false;
96 246
97 bool is_combo_active = is_active; 247 // state to check against so we find the last key of the combo from the buffer
248#if defined(EXTRA_EXTRA_LONG_COMBOS)
249 uint32_t state = 0;
250#elif defined(EXTRA_LONG_COMBOS)
251 uint16_t state = 0;
252#else
253 uint8_t state = 0;
254#endif
255
256 for (uint8_t key_buffer_i = 0; key_buffer_i < key_buffer_size; key_buffer_i++) {
257
258 queued_record_t *qrecord = &key_buffer[key_buffer_i];
259 keyrecord_t *record = &qrecord->record;
260 uint16_t keycode = qrecord->keycode;
261
262 uint8_t key_count = 0;
263 uint16_t key_index = -1;
264 _find_key_index_and_count(combo->keys, keycode, &key_index, &key_count);
265
266 if (-1 == (int16_t)key_index) {
267 // key not part of this combo
268 continue;
269 }
98 270
99 if (record->event.pressed) { 271 KEY_STATE_DOWN(state, key_index);
100 KEY_STATE_DOWN(index); 272 if (ALL_COMBO_KEYS_ARE_DOWN(state, key_count)) {
273 // this in the end executes the combo when the key_buffer is dumped.
274 record->keycode = combo->keycode;
275 record->event.key = COMBO_KEY_POS;
101 276
102 if (is_combo_active) { 277 qrecord->combo_index = combo_index;
103 if (ALL_COMBO_KEYS_ARE_DOWN) { /* Combo was pressed */ 278 ACTIVATE_COMBO(combo);
104 send_combo(combo->keycode, true); 279
105 drop_buffer = true; 280 break;
281 } else {
282 // key was part of the combo but not the last one, "disable" it
283 // by making it a TICK event.
284 record->event.time = 0;
285 }
286
287 }
288 drop_combo_from_buffer(combo_index);
289}
290
291static inline void apply_combos(void) {
292 // Apply all buffered normal combos.
293 for (uint8_t i = combo_buffer_read;
294 i != combo_buffer_write;
295 INCREMENT_MOD(i)) {
296
297 queued_combo_t *buffered_combo = &combo_buffer[i];
298 combo_t *combo = &key_combos[buffered_combo->combo_index];
299
300#ifdef COMBO_MUST_TAP_PER_COMBO
301 if (get_combo_must_tap(buffered_combo->combo_index, combo)) {
302 // Tap-only combos are applied on key release only, so let's drop 'em here.
303 drop_combo_from_buffer(buffered_combo->combo_index);
304 continue;
305 }
306#endif
307 apply_combo(buffered_combo->combo_index, combo);
308 }
309 dump_key_buffer();
310 clear_combos();
311}
312
313combo_t* overlaps(combo_t *combo1, combo_t *combo2) {
314 /* Checks if the combos overlap and returns the combo that should be
315 * dropped from the combo buffer.
316 * The combo that has less keys will be dropped. If they have the same
317 * amount of keys, drop combo1. */
318
319 uint8_t idx1 = 0, idx2 = 0;
320 uint16_t key1, key2;
321 bool overlaps = false;
322
323 while ((key1 = pgm_read_word(&combo1->keys[idx1])) != COMBO_END) {
324 idx2 = 0;
325 while ((key2 = pgm_read_word(&combo2->keys[idx2])) != COMBO_END) {
326 if (key1 == key2) overlaps = true;
327 idx2 += 1;
328 }
329 idx1 += 1;
330 }
331
332 if (!overlaps) return NULL;
333 if (idx2 < idx1) return combo2;
334 return combo1;
335}
336
337static bool process_single_combo(combo_t *combo, uint16_t keycode, keyrecord_t *record, uint16_t combo_index) {
338 uint8_t key_count = 0;
339 uint16_t key_index = -1;
340 _find_key_index_and_count(combo->keys, keycode, &key_index, &key_count);
341
342 /* Continue processing if key isn't part of current combo. */
343 if (-1 == (int16_t)key_index) {
344 return false;
345 }
346
347 bool key_is_part_of_combo = !COMBO_DISABLED(combo) && is_combo_enabled();
348
349 if (record->event.pressed && key_is_part_of_combo) {
350 uint16_t time = _get_combo_term(combo_index, combo);
351 if (!COMBO_ACTIVE(combo)) {
352 KEY_STATE_DOWN(combo->state, key_index);
353 if (longest_term < time) {
354 longest_term = time;
106 } 355 }
107 } 356 }
357 if (ALL_COMBO_KEYS_ARE_DOWN(COMBO_STATE(combo), key_count)) {
358 /* Combo was fully pressed */
359 /* Buffer the combo so we can fire it after COMBO_TERM */
360
361#ifndef COMBO_NO_TIMER
362 /* Don't buffer this combo if its combo term has passed. */
363 if (timer && timer_elapsed(timer) > time) {
364 DISABLE_COMBO(combo);
365 return true;
366 } else
367#endif
368 {
369
370 // disable readied combos that overlap with this combo
371 combo_t *drop = NULL;
372 for (uint8_t combo_buffer_i = combo_buffer_read;
373 combo_buffer_i != combo_buffer_write;
374 INCREMENT_MOD(combo_buffer_i)) {
375
376 queued_combo_t *qcombo = &combo_buffer[combo_buffer_i];
377 combo_t *buffered_combo = &key_combos[qcombo->combo_index];
378
379 if ((drop = overlaps(buffered_combo, combo))) {
380 DISABLE_COMBO(drop);
381 if (drop == combo) {
382 // stop checking for overlaps if dropped combo was current combo.
383 break;
384 } else if (combo_buffer_i == combo_buffer_read && drop == buffered_combo) {
385 /* Drop the disabled buffered combo from the buffer if
386 * it is in the beginning of the buffer. */
387 INCREMENT_MOD(combo_buffer_read);
388 }
389 }
390
391 }
392
393 if (drop != combo) {
394 // save this combo to buffer
395 combo_buffer[combo_buffer_write] = (queued_combo_t){
396 .combo_index=combo_index,
397 };
398 INCREMENT_MOD(combo_buffer_write);
399
400 // get possible longer waiting time for tap-/hold-only combos.
401 longest_term = _get_wait_time(combo_index, combo);
402 }
403 } // if timer elapsed end
404
405 }
108 } else { 406 } else {
109 if (ALL_COMBO_KEYS_ARE_DOWN) { /* Combo was released */ 407 // chord releases
110 send_combo(combo->keycode, false); 408 if (!COMBO_ACTIVE(combo) && ALL_COMBO_KEYS_ARE_DOWN(COMBO_STATE(combo), key_count)) {
409 /* First key quickly released */
410 if (COMBO_DISABLED(combo) || _get_combo_must_hold(combo_index, combo)) {
411 // combo wasn't tappable, disable it and drop it from buffer.
412 drop_combo_from_buffer(combo_index);
413 key_is_part_of_combo = false;
414 }
415#ifdef COMBO_MUST_TAP_PER_COMBO
416 else if (get_combo_must_tap(combo_index, combo)) {
417 // immediately apply tap-only combo
418 apply_combo(combo_index, combo);
419 apply_combos(); // also apply other prepared combos and dump key buffer
420# ifdef COMBO_PROCESS_KEY_RELEASE
421 if (process_combo_key_release(combo_index, combo, key_index, keycode)) {
422 release_combo(combo_index, combo);
423 }
424# endif
425 }
426#endif
427 } else if (COMBO_ACTIVE(combo)
428 && ONLY_ONE_KEY_IS_DOWN(COMBO_STATE(combo))
429 && KEY_NOT_YET_RELEASED(COMBO_STATE(combo), key_index)
430 ) {
431 /* last key released */
432 release_combo(combo_index, combo);
433 key_is_part_of_combo = true;
434
435#ifdef COMBO_PROCESS_KEY_RELEASE
436 process_combo_key_release(combo_index, combo, key_index, keycode);
437#endif
438 } else if (COMBO_ACTIVE(combo)
439 && KEY_NOT_YET_RELEASED(COMBO_STATE(combo), key_index)
440 ) {
441 /* first or middle key released */
442 key_is_part_of_combo = true;
443
444#ifdef COMBO_PROCESS_KEY_RELEASE
445 if (process_combo_key_release(combo_index, combo, key_index, keycode)) {
446 release_combo(combo_index, combo);
447 }
448#endif
111 } else { 449 } else {
112 /* continue processing without immediately returning */ 450 /* The released key was part of an incomplete combo */
113 is_combo_active = false; 451 key_is_part_of_combo = false;
114 } 452 }
115 453
116 KEY_STATE_UP(index); 454 KEY_STATE_UP(combo->state, key_index);
117 } 455 }
118 456
119 return is_combo_active; 457 return key_is_part_of_combo;
120} 458}
121 459
122#define NO_COMBO_KEYS_ARE_DOWN (0 == combo->state)
123
124bool process_combo(uint16_t keycode, keyrecord_t *record) { 460bool process_combo(uint16_t keycode, keyrecord_t *record) {
125 bool is_combo_key = false; 461 bool is_combo_key = false;
126 drop_buffer = false;
127 bool no_combo_keys_pressed = true; 462 bool no_combo_keys_pressed = true;
128 463
129 if (keycode == CMB_ON && record->event.pressed) { 464 if (keycode == CMB_ON && record->event.pressed) {
@@ -141,65 +476,82 @@ bool process_combo(uint16_t keycode, keyrecord_t *record) {
141 return true; 476 return true;
142 } 477 }
143 478
144 if (!is_combo_enabled()) { 479#ifdef COMBO_ONLY_FROM_LAYER
145 return true; 480 /* Only check keycodes from one layer. */
146 } 481 keycode = keymap_key_to_keycode(COMBO_ONLY_FROM_LAYER, record->event.key);
147#ifndef COMBO_VARIABLE_LEN
148 for (current_combo_index = 0; current_combo_index < COMBO_COUNT; ++current_combo_index) {
149#else
150 for (current_combo_index = 0; current_combo_index < COMBO_LEN; ++current_combo_index) {
151#endif 482#endif
152 combo_t *combo = &key_combos[current_combo_index]; 483
153 is_combo_key |= process_single_combo(combo, keycode, record); 484 for (uint16_t idx = 0; idx < COMBO_LEN; ++idx) {
154 no_combo_keys_pressed = no_combo_keys_pressed && NO_COMBO_KEYS_ARE_DOWN; 485 combo_t *combo = &key_combos[idx];
486 is_combo_key |= process_single_combo(combo, keycode, record, idx);
487 no_combo_keys_pressed = no_combo_keys_pressed && (NO_COMBO_KEYS_ARE_DOWN || COMBO_ACTIVE(combo) || COMBO_DISABLED(combo));
155 } 488 }
156 489
157 if (drop_buffer) { 490 if (record->event.pressed && is_combo_key) {
158 /* buffer is only dropped when we complete a combo, so we refresh the timer 491#ifndef COMBO_NO_TIMER
159 * here */ 492# ifdef COMBO_STRICT_TIMER
160 timer = timer_read(); 493 if (!timer) {
161 dump_key_buffer(false); 494 // timer is set only on the first key
162 } else if (!is_combo_key) { 495 timer = timer_read();
163 /* if no combos claim the key we need to emit the keybuffer */ 496 }
164 dump_key_buffer(true); 497# else
165
166 // reset state if there are no combo keys pressed at all
167 if (no_combo_keys_pressed) {
168 timer = 0;
169 is_active = true;
170 }
171 } else if (record->event.pressed && is_active) {
172 /* otherwise the key is consumed and placed in the buffer */
173 timer = timer_read(); 498 timer = timer_read();
499# endif
500#endif
174 501
175 if (buffer_size < MAX_COMBO_LENGTH) { 502 if (key_buffer_size < COMBO_KEY_BUFFER_LENGTH) {
176#ifdef COMBO_ALLOW_ACTION_KEYS 503 key_buffer[key_buffer_size++] = (queued_record_t){
177 key_buffer[buffer_size++] = *record; 504 .record = *record,
178#else 505 .keycode = keycode,
179 key_buffer[buffer_size++] = keycode; 506 .combo_index = -1, // this will be set when applying combos
507 };
508 }
509 } else {
510 if (combo_buffer_read != combo_buffer_write) {
511 // some combo is prepared
512 apply_combos();
513 } else {
514 // reset state if there are no combo keys pressed at all
515 dump_key_buffer();
516#ifndef COMBO_NO_TIMER
517 timer = 0;
180#endif 518#endif
519 clear_combos();
181 } 520 }
182 } 521 }
183
184 return !is_combo_key; 522 return !is_combo_key;
185} 523}
186 524
187void matrix_scan_combo(void) { 525void combo_task(void) {
188 if (b_combo_enable && is_active && timer && timer_elapsed(timer) > COMBO_TERM) { 526 if (!b_combo_enable) {
189 /* This disables the combo, meaning key events for this 527 return;
190 * combo will be handled by the next processors in the chain
191 */
192 is_active = false;
193 dump_key_buffer(true);
194 } 528 }
529
530#ifndef COMBO_NO_TIMER
531 if (timer && timer_elapsed(timer) > longest_term) {
532 if (combo_buffer_read != combo_buffer_write) {
533 apply_combos();
534 longest_term = 0;
535 timer = 0;
536 } else {
537 dump_key_buffer();
538 timer = 0;
539 clear_combos();
540 }
541 }
542#endif
195} 543}
196 544
197void combo_enable(void) { b_combo_enable = true; } 545void combo_enable(void) { b_combo_enable = true; }
198 546
199void combo_disable(void) { 547void combo_disable(void) {
200 b_combo_enable = is_active = false; 548#ifndef COMBO_NO_TIMER
201 timer = 0; 549 timer = 0;
202 dump_key_buffer(true); 550#endif
551 b_combo_enable = false;
552 combo_buffer_read = combo_buffer_write;
553 clear_combos();
554 dump_key_buffer();
203} 555}
204 556
205void combo_toggle(void) { 557void combo_toggle(void) {
diff --git a/quantum/process_keycode/process_combo.h b/quantum/process_keycode/process_combo.h
index e51a2f1f4..43c36d79e 100644
--- a/quantum/process_keycode/process_combo.h
+++ b/quantum/process_keycode/process_combo.h
@@ -20,23 +20,38 @@
20#include "quantum.h" 20#include "quantum.h"
21#include <stdint.h> 21#include <stdint.h>
22 22
23#ifdef EXTRA_EXTRA_LONG_COMBOS 23#ifdef EXTRA_SHORT_COMBOS
24# define MAX_COMBO_LENGTH 6
25#elif defined(EXTRA_EXTRA_LONG_COMBOS)
24# define MAX_COMBO_LENGTH 32 26# define MAX_COMBO_LENGTH 32
25#elif EXTRA_LONG_COMBOS 27#elif defined(EXTRA_LONG_COMBOS)
26# define MAX_COMBO_LENGTH 16 28# define MAX_COMBO_LENGTH 16
27#else 29#else
28# define MAX_COMBO_LENGTH 8 30# define MAX_COMBO_LENGTH 8
29#endif 31#endif
30 32
33#ifndef COMBO_KEY_BUFFER_LENGTH
34# define COMBO_KEY_BUFFER_LENGTH MAX_COMBO_LENGTH
35#endif
36#ifndef COMBO_BUFFER_LENGTH
37# define COMBO_BUFFER_LENGTH 4
38#endif
39
31typedef struct { 40typedef struct {
32 const uint16_t *keys; 41 const uint16_t *keys;
33 uint16_t keycode; 42 uint16_t keycode;
34#ifdef EXTRA_EXTRA_LONG_COMBOS 43#ifdef EXTRA_SHORT_COMBOS
44 uint8_t state;
45#else
46 bool disabled;
47 bool active;
48# if defined(EXTRA_EXTRA_LONG_COMBOS)
35 uint32_t state; 49 uint32_t state;
36#elif EXTRA_LONG_COMBOS 50# elif defined(EXTRA_LONG_COMBOS)
37 uint16_t state; 51 uint16_t state;
38#else 52# else
39 uint8_t state; 53 uint8_t state;
54# endif
40#endif 55#endif
41} combo_t; 56} combo_t;
42 57
@@ -46,15 +61,18 @@ typedef struct {
46 { .keys = &(ck)[0] } 61 { .keys = &(ck)[0] }
47 62
48#define COMBO_END 0 63#define COMBO_END 0
49#ifndef COMBO_COUNT
50# define COMBO_COUNT 0
51#endif
52#ifndef COMBO_TERM 64#ifndef COMBO_TERM
53# define COMBO_TERM TAPPING_TERM 65# define COMBO_TERM 50
54#endif 66#endif
67#ifndef COMBO_HOLD_TERM
68# define COMBO_HOLD_TERM TAPPING_TERM
69#endif
70
71/* check if keycode is only modifiers */
72#define KEYCODE_IS_MOD(code) (IS_MOD(code) || (code >= QK_MODS && code <= QK_MODS_MAX && !(code & QK_BASIC_MAX)))
55 73
56bool process_combo(uint16_t keycode, keyrecord_t *record); 74bool process_combo(uint16_t keycode, keyrecord_t *record);
57void matrix_scan_combo(void); 75void combo_task(void);
58void process_combo_event(uint16_t combo_index, bool pressed); 76void process_combo_event(uint16_t combo_index, bool pressed);
59 77
60void combo_enable(void); 78void combo_enable(void);
diff --git a/quantum/process_keycode/process_haptic.c b/quantum/process_keycode/process_haptic.c
new file mode 100644
index 000000000..29a4ffd10
--- /dev/null
+++ b/quantum/process_keycode/process_haptic.c
@@ -0,0 +1,147 @@
1/* Copyright 2021 QMK
2 *
3 * This program is free software: you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License as published by
5 * the Free Software Foundation, either version 2 of the License, or
6 * (at your option) any later version.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 */
16#include "haptic.h"
17#include "process_haptic.h"
18#include "quantum_keycodes.h"
19
20__attribute__((weak)) bool get_haptic_enabled_key(uint16_t keycode, keyrecord_t *record) {
21 switch (keycode) {
22#ifdef NO_HAPTIC_MOD
23 case QK_MOD_TAP ... QK_MOD_TAP_MAX:
24 if (record->tap.count == 0) return false;
25 break;
26 case QK_LAYER_TAP_TOGGLE ... QK_LAYER_TAP_TOGGLE_MAX:
27 if (record->tap.count != TAPPING_TOGGLE) return false;
28 break;
29 case QK_LAYER_TAP ... QK_LAYER_TAP_MAX:
30 if (record->tap.count == 0) return false;
31 break;
32 case KC_LCTRL ... KC_RGUI:
33 case QK_MOMENTARY ... QK_MOMENTARY_MAX:
34#endif
35#ifdef NO_HAPTIC_FN
36 case KC_FN0 ... KC_FN31:
37#endif
38#ifdef NO_HAPTIC_ALPHA
39 case KC_A ... KC_Z:
40#endif
41#ifdef NO_HAPTIC_PUNCTUATION
42 case KC_ENTER:
43 case KC_ESCAPE:
44 case KC_BSPACE:
45 case KC_SPACE:
46 case KC_MINUS:
47 case KC_EQUAL:
48 case KC_LBRACKET:
49 case KC_RBRACKET:
50 case KC_BSLASH:
51 case KC_NONUS_HASH:
52 case KC_SCOLON:
53 case KC_QUOTE:
54 case KC_GRAVE:
55 case KC_COMMA:
56 case KC_SLASH:
57 case KC_DOT:
58 case KC_NONUS_BSLASH:
59#endif
60#ifdef NO_HAPTIC_LOCKKEYS
61 case KC_CAPSLOCK:
62 case KC_SCROLLLOCK:
63 case KC_NUMLOCK:
64#endif
65#ifdef NO_HAPTIC_NAV
66 case KC_PSCREEN:
67 case KC_PAUSE:
68 case KC_INSERT:
69 case KC_DELETE:
70 case KC_PGDOWN:
71 case KC_PGUP:
72 case KC_LEFT:
73 case KC_UP:
74 case KC_RIGHT:
75 case KC_DOWN:
76 case KC_END:
77 case KC_HOME:
78#endif
79#ifdef NO_HAPTIC_NUMERIC
80 case KC_1 ... KC_0:
81#endif
82 return false;
83 }
84 return true;
85}
86
87bool process_haptic(uint16_t keycode, keyrecord_t *record) {
88 if (record->event.pressed) {
89 switch (keycode) {
90 case HPT_ON:
91 haptic_enable();
92 break;
93 case HPT_OFF:
94 haptic_disable();
95 break;
96 case HPT_TOG:
97 haptic_toggle();
98 break;
99 case HPT_RST:
100 haptic_reset();
101 break;
102 case HPT_FBK:
103 haptic_feedback_toggle();
104 break;
105 case HPT_BUZ:
106 haptic_buzz_toggle();
107 break;
108 case HPT_MODI:
109 haptic_mode_increase();
110 break;
111 case HPT_MODD:
112 haptic_mode_decrease();
113 break;
114 case HPT_DWLI:
115 haptic_dwell_increase();
116 break;
117 case HPT_DWLD:
118 haptic_dwell_decrease();
119 break;
120 case HPT_CONT:
121 haptic_toggle_continuous();
122 break;
123 case HPT_CONI:
124 haptic_cont_increase();
125 break;
126 case HPT_COND:
127 haptic_cont_decrease();
128 break;
129 }
130 }
131
132 if (haptic_get_enable()) {
133 if (record->event.pressed) {
134 // keypress
135 if (haptic_get_feedback() < 2 && get_haptic_enabled_key(keycode, record)) {
136 haptic_play();
137 }
138 } else {
139 // keyrelease
140 if (haptic_get_feedback() > 0 && get_haptic_enabled_key(keycode, record)) {
141 haptic_play();
142 }
143 }
144 }
145
146 return true;
147}
diff --git a/quantum/process_keycode/process_haptic.h b/quantum/process_keycode/process_haptic.h
new file mode 100644
index 000000000..6dbb0f014
--- /dev/null
+++ b/quantum/process_keycode/process_haptic.h
@@ -0,0 +1,21 @@
1/* Copyright 2021 QMK
2 *
3 * This program is free software: you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License as published by
5 * the Free Software Foundation, either version 2 of the License, or
6 * (at your option) any later version.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 */
16#pragma once
17
18#include <stdbool.h>
19#include "action.h"
20
21bool process_haptic(uint16_t keycode, keyrecord_t *record);
diff --git a/quantum/process_keycode/process_key_override.c b/quantum/process_keycode/process_key_override.c
new file mode 100644
index 000000000..09b272507
--- /dev/null
+++ b/quantum/process_keycode/process_key_override.c
@@ -0,0 +1,518 @@
1/*
2 * Copyright 2021 Jonas Gessner
3 *
4 * This program is free software: you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation, either version 2 of the License, or
7 * (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program. If not, see <http://www.gnu.org/licenses/>.
16 */
17
18#include "quantum.h"
19#include "report.h"
20#include "timer.h"
21#include "process_key_override.h"
22
23#include <debug.h>
24
25#ifndef KEY_OVERRIDE_REPEAT_DELAY
26# define KEY_OVERRIDE_REPEAT_DELAY 500
27#endif
28
29// For benchmarking the time it takes to call process_key_override on every key press (needs keyboard debugging enabled as well)
30// #define BENCH_KEY_OVERRIDE
31
32// For debug output (needs keyboard debugging enabled as well)
33// #define DEBUG_KEY_OVERRIDE
34
35#ifdef DEBUG_KEY_OVERRIDE
36# define key_override_printf dprintf
37#else
38# define key_override_printf(str, ...) \
39 {}
40#endif
41
42// Helpers
43
44// Private functions implemented elsewhere in qmk/tmk
45extern uint8_t extract_mod_bits(uint16_t code);
46extern void set_weak_override_mods(uint8_t mods);
47extern void clear_weak_override_mods(void);
48extern void set_suppressed_override_mods(uint8_t mods);
49extern void clear_suppressed_override_mods(void);
50
51static uint16_t clear_mods_from(uint16_t keycode) {
52 switch (keycode) {
53 case QK_MODS ... QK_MODS_MAX:
54 break;
55 default:
56 return keycode;
57 }
58
59 static const uint16_t all_mods = QK_LCTL | QK_LSFT | QK_LALT | QK_LGUI | QK_RCTL | QK_RSFT | QK_RALT | QK_RGUI;
60
61 return (keycode & ~(all_mods));
62}
63
64// Internal variables
65static const key_override_t *active_override = NULL;
66static bool active_override_trigger_is_down = false;
67
68// Used to keep track of what non-modifier key was last pressed down. We never want to activate an override for a trigger key that is not the last non-mod key that was pressed down. OSes internally completely unregister a key that is held when a different key is held down after. We want to respect this here.
69static uint16_t last_key_down = 0;
70// When was the last key pressed down?
71static uint32_t last_key_down_time = 0;
72
73// What timestamp are we comparing to when waiting to register a deferred key?
74static uint32_t defer_reference_time = 0;
75// What delay should pass until deferred key is registered?
76static uint32_t defer_delay = 0;
77
78// Holds the keycode that should be registered at a later time, in order to not get false key presses
79static uint16_t deferred_register = 0;
80
81// TODO: in future maybe save in EEPROM?
82static bool enabled = true;
83
84// Public variables
85__attribute__((weak)) const key_override_t **key_overrides = NULL;
86
87// Forward decls
88static const key_override_t *clear_active_override(const bool allow_reregister);
89
90void key_override_on(void) {
91 enabled = true;
92 key_override_printf("Key override ON\n");
93}
94
95void key_override_off(void) {
96 enabled = false;
97 clear_active_override(false);
98 key_override_printf("Key override OFF\n");
99}
100
101void key_override_toggle(void) {
102 if (key_override_is_enabled()) {
103 key_override_off();
104 } else {
105 key_override_on();
106 }
107}
108
109bool key_override_is_enabled(void) { return enabled; }
110
111// Returns whether the modifiers that are pressed are such that the override should activate
112static bool key_override_matches_active_modifiers(const key_override_t *override, const uint8_t mods) {
113 // Check that negative keys pass
114 if ((override->negative_mod_mask & mods) != 0) {
115 return false;
116 }
117
118 // Immediately return true if the override requires no mods down
119 if (override->trigger_mods == 0) {
120 return true;
121 }
122
123 if ((override->options & ko_option_one_mod) != 0) {
124 // At least one of the trigger modifiers must be down
125 return (override->trigger_mods & mods) != 0;
126 } else {
127 // All trigger modifiers must be down, but each mod can be active on either side (if both sides are specified).
128
129 // Which mods, regardless of side, are required?
130 uint8_t one_sided_required_mods = (override->trigger_mods & 0b1111) | (override->trigger_mods >> 4);
131
132 // Which of the required modifiers are active?
133 uint8_t active_required_mods = override->trigger_mods & mods;
134
135 // Move the active requird mods to one side
136 uint8_t one_sided_active_required_mods = (active_required_mods & 0b1111) | (active_required_mods >> 4);
137
138 // Check that there is a full match between the required one-sided mods and active required one sided mods
139 return one_sided_active_required_mods == one_sided_required_mods;
140 }
141
142 return false;
143}
144
145static void schedule_deferred_register(const uint16_t keycode) {
146 if (timer_elapsed32(last_key_down_time) < KEY_OVERRIDE_REPEAT_DELAY) {
147 // Defer until KEY_OVERRIDE_REPEAT_DELAY has passed since the trigger key was pressed down. This emulates the behavior as holding down a key x, then holding down shift shortly after. Usually the shifted key X is not immediately produced, but rather a 'key repeat delay' passes before any repeated character is output.
148 defer_reference_time = last_key_down_time;
149 defer_delay = KEY_OVERRIDE_REPEAT_DELAY;
150 } else {
151 // Wait a very short time when a modifier event triggers the override to avoid false activations when e.g. a modifier is pressed just before a key is released (with the intention of pairing the modifier with a different key), or a modifier is lifted shortly before the trigger key is lifted. Operating systems by default reject modifier-events that happen very close to a non-modifier event.
152 defer_reference_time = timer_read32();
153 defer_delay = 50; // 50ms
154 }
155 deferred_register = keycode;
156}
157
158const key_override_t *clear_active_override(const bool allow_reregister) {
159 if (active_override == NULL) {
160 return NULL;
161 }
162
163 key_override_printf("Deactivating override\n");
164
165 deferred_register = 0;
166
167 // Clear the suppressed mods
168 clear_suppressed_override_mods();
169
170 // Unregister the replacement. First remove the weak override mods
171 clear_weak_override_mods();
172
173 const key_override_t *const old = active_override;
174
175 const uint8_t mod_free_replacement = clear_mods_from(active_override->replacement);
176
177 bool unregister_replacement = mod_free_replacement != KC_NO && // KC_NO is never registered
178 mod_free_replacement < SAFE_RANGE; // Custom keycodes are never registered
179
180 // Try firing the custom handler
181 if (active_override->custom_action != NULL) {
182 unregister_replacement &= active_override->custom_action(false, active_override->context);
183 }
184
185 // Then unregister the mod-free replacement key if desired
186 if (unregister_replacement) {
187 if (IS_KEY(mod_free_replacement)) {
188 del_key(mod_free_replacement);
189 } else {
190 key_override_printf("NOT KEY 1\n");
191 send_keyboard_report();
192 unregister_code(mod_free_replacement);
193 }
194 }
195
196 const uint16_t trigger = active_override->trigger;
197
198 const bool reregister_trigger = allow_reregister && // Check if allowed from caller
199 (active_override->options & ko_option_no_reregister_trigger) == 0 && // Check if override allows
200 active_override_trigger_is_down && // Check if trigger is even down
201 trigger != KC_NO && // KC_NO is never registered
202 trigger < SAFE_RANGE; // A custom keycode should not be registered
203
204 // Optionally re-register the trigger if it is still down
205 if (reregister_trigger) {
206 key_override_printf("Re-registering trigger deferred: %u\n", trigger);
207
208 // This will always be a modifier event, so defer always
209 schedule_deferred_register(trigger);
210 }
211
212 send_keyboard_report();
213
214 active_override = NULL;
215 active_override_trigger_is_down = false;
216
217 return old;
218}
219
220/** Checks if the key event is an allowed activation event for the provided override. Does not check things like whether the correct mods or correct trigger key is down. */
221static bool check_activation_event(const key_override_t *override, const bool key_down, const bool is_mod) {
222 ko_option_t options = override->options;
223
224 if ((options & ko_options_all_activations) == 0) {
225 // No activation option provided at all. This is wrong, but let's assume the default activations (ko_options_all_activations) were meant...
226 options = ko_options_all_activations;
227 }
228
229 if (is_mod) {
230 if (key_down) {
231 return (options & ko_option_activation_required_mod_down) != 0;
232 } else {
233 return (options & ko_option_activation_negative_mod_up) != 0;
234 }
235 } else {
236 if (key_down) {
237 return (options & ko_option_activation_trigger_down) != 0;
238 } else {
239 return false;
240 }
241 }
242}
243
244/** Iterates through the list of key overrides and tries activating each, until it finds one that activates or reaches the end of overrides. Returns true if the key action for `keycode` should be sent */
245static bool try_activating_override(const uint16_t keycode, const uint8_t layer, const bool key_down, const bool is_mod, const uint8_t active_mods, bool *activated) {
246 if (key_overrides == NULL) {
247 return true;
248 }
249
250 for (uint8_t i = 0;; i++) {
251 const key_override_t *const override = key_overrides[i];
252
253 // End of array
254 if (override == NULL) {
255 break;
256 }
257
258 // Fast, but not full mods check. Most key presses will not have any mods down, and most overrides will require mods. Hence here we filter overrides that require mods to be down while no mods are down
259 if (active_mods == 0 && override->trigger_mods != 0) {
260 key_override_printf("Not activating override: Modifiers don't match\n");
261 continue;
262 }
263
264 // Check layer
265 if ((override->layers & (1 << layer)) == 0) {
266 key_override_printf("Not activating override: Not set to activate on pressed layer\n");
267 continue;
268 }
269
270 // Check allowed activation events
271 if (!check_activation_event(override, key_down, is_mod)) {
272 key_override_printf("Not activating override: Activation event not allowed\n");
273 continue;
274 }
275
276 const bool is_trigger = override->trigger == keycode;
277
278 // Check if trigger lifted. This is a small optimization in order to skip the remaining checks
279 if (is_trigger && !key_down) {
280 key_override_printf("Not activating override: Trigger lifted\n");
281 continue;
282 }
283
284 // If the trigger is KC_NO it means 'no key', so only the required modifiers need to be down.
285 const bool no_trigger = override->trigger == KC_NO;
286
287 // Check if aleady active
288 if (override == active_override) {
289 key_override_printf("Not activating override: Alerady actived\n");
290 continue;
291 }
292
293 // Check if enabled
294 if (override->enabled != NULL && !((*(override->enabled) & 1))) {
295 key_override_printf("Not activating override: Not enabled\n");
296 continue;
297 }
298
299 // Check mods precisely
300 if (!key_override_matches_active_modifiers(override, active_mods)) {
301 key_override_printf("Not activating override: Modifiers don't match\n");
302 continue;
303 }
304
305 // Check if trigger key is down.
306 const bool trigger_down = is_trigger && key_down;
307
308 // At this point, all requirements for activation are checked, except whether the trigger key is pressed. Now we check if the required trigger is down
309 // If no trigger key is required, yes.
310 // If the trigger was just pressed, yes.
311 // If the last non-mod key that was pressed down is the trigger key, yes.
312 bool should_activate = no_trigger || trigger_down || last_key_down == override->trigger;
313
314 if (!should_activate) {
315 key_override_printf("Not activating override. Trigger not down\n");
316 continue;
317 }
318
319 key_override_printf("Activating override\n");
320
321 clear_active_override(false);
322
323 active_override = override;
324 active_override_trigger_is_down = true;
325
326 set_suppressed_override_mods(override->suppressed_mods);
327
328 if (!trigger_down && !no_trigger) {
329 // When activating a key override the trigger is is always unregistered. In the case where the key that newly pressed is not the trigger key, we have to explicitly remove the trigger key from the keyboard report. If the trigger was just pressed down we simply suppress the event which also has the effect of the trigger key not being registered in the keyboard report.
330 if (IS_KEY(override->trigger)) {
331 del_key(override->trigger);
332 } else {
333 unregister_code(override->trigger);
334 }
335 }
336
337 const uint16_t mod_free_replacement = clear_mods_from(override->replacement);
338
339 bool register_replacement = mod_free_replacement != KC_NO && // KC_NO is never registered
340 mod_free_replacement < SAFE_RANGE; // Custom keycodes are never registered
341
342 // Try firing the custom handler
343 if (override->custom_action != NULL) {
344 register_replacement &= override->custom_action(true, override->context);
345 }
346
347 if (register_replacement) {
348 const uint8_t override_mods = extract_mod_bits(override->replacement);
349 set_weak_override_mods(override_mods);
350
351 // If this is a modifier event that activates the key override we _always_ defer the actual full activation of the override
352 if (is_mod) {
353 key_override_printf("Deferring register replacement key\n");
354 schedule_deferred_register(mod_free_replacement);
355 send_keyboard_report();
356 } else {
357 if (IS_KEY(mod_free_replacement)) {
358 add_key(mod_free_replacement);
359 } else {
360 key_override_printf("NOT KEY 2\n");
361 send_keyboard_report();
362 // On macOS there seems to be a race condition when it comes to the keyboard report and consumer keycodes. It seems the OS may recognize a consumer keycode before an updated keyboard report, even if the keyboard report is actually sent before the consumer key. I assume it is some sort of race condition because it happens infrequently and very irregularly. Waiting for about at least 10ms between sending the keyboard report and sending the consumer code has shown to fix this.
363 wait_ms(10);
364 register_code(mod_free_replacement);
365 }
366 }
367 } else {
368 // If not registering the replacement key send keyboard report to update the unregistered keys.
369 send_keyboard_report();
370 }
371
372 *activated = true;
373
374 // If the trigger is down, suppress the event so that it does not get added to the keyboard report.
375 return !trigger_down;
376 }
377
378 *activated = false;
379
380 return true;
381}
382
383void key_override_task(void) {
384 if (deferred_register == 0) {
385 return;
386 }
387
388 if (timer_elapsed32(defer_reference_time) >= defer_delay) {
389 key_override_printf("Registering deferred key\n");
390 register_code16(deferred_register);
391 deferred_register = 0;
392 defer_reference_time = 0;
393 defer_delay = 0;
394 }
395}
396
397bool process_key_override(const uint16_t keycode, const keyrecord_t *const record) {
398#ifdef BENCH_KEY_OVERRIDE
399 uint16_t start = timer_read();
400#endif
401
402 const bool key_down = record->event.pressed;
403 const bool is_mod = IS_MOD(keycode);
404
405 if (key_down) {
406 switch (keycode) {
407 case KEY_OVERRIDE_TOGGLE:
408 key_override_toggle();
409 return false;
410
411 case KEY_OVERRIDE_ON:
412 key_override_on();
413 return false;
414
415 case KEY_OVERRIDE_OFF:
416 key_override_off();
417 return false;
418
419 default:
420 break;
421 }
422 }
423
424 if (!enabled) {
425 return true;
426 }
427
428 uint8_t effective_mods = get_mods();
429
430#ifdef KEY_OVERRIDE_INCLUDE_WEAK_MODS
431 effective_mods |= get_weak_mods();
432#endif
433
434#ifndef NO_ACTION_ONESHOT
435 // Locked one shot mods are added to get_mods(), I think (why??) while oneshot mods are in get_oneshot_mods(). Still OR with get_locked_oneshot_mods because that's where those mods _should_ be saved.
436 effective_mods |= get_oneshot_locked_mods() | get_oneshot_mods();
437#endif
438
439 if (is_mod) {
440 // The mods returned from get_mods() will be updated with this new event _after_ this code runs. Hence we manually update the effective mods here to really know the effective mods.
441 if (key_down) {
442 effective_mods |= MOD_BIT(keycode);
443 } else {
444 effective_mods &= ~MOD_BIT(keycode);
445 }
446 } else {
447 if (key_down) {
448 last_key_down = keycode;
449 last_key_down_time = timer_read32();
450 deferred_register = 0;
451 }
452
453 // The last key that was pressed was just released. No more keys are therefore sending input
454 if (!key_down && keycode == last_key_down) {
455 last_key_down = 0;
456 last_key_down_time = 0;
457 // We also cancel any deferred registers because, again, no keys are sending any input. Only the last key that is pressed creates an input – this key was just lifted.
458 deferred_register = 0;
459 }
460 }
461
462 key_override_printf("key down: %u keycode: %u is mod: %u effective mods: %u\n", key_down, keycode, is_mod, effective_mods);
463
464 bool send_key_action = true;
465 bool activated = false;
466
467 // Non-mod key up events never activate a key override
468 if (is_mod || key_down) {
469 // Get the exact layer that was hit. It will be cached at this point
470 const uint8_t layer = read_source_layers_cache(record->event.key);
471
472 // Use blocked to ensure the same override is not activated again immediately after it is deactivated
473 send_key_action = try_activating_override(keycode, layer, key_down, is_mod, effective_mods, &activated);
474
475 if (!send_key_action) {
476 send_keyboard_report();
477 }
478 }
479
480 if (!activated && active_override != NULL) {
481 if (is_mod) {
482 // Check if necessary modifier of current override goes up or a negative mod goes down
483 if (!key_override_matches_active_modifiers(active_override, effective_mods)) {
484 key_override_printf("Deactivating override because necessary modifier lifted or negative mod pressed\n");
485 clear_active_override(true);
486 }
487 } else {
488 // Check if trigger of current override goes up or if override does not allow additional keys to be down and another key goes down
489 const bool is_trigger = keycode == active_override->trigger;
490 bool should_deactivate = false;
491
492 // Check if trigger key lifted
493 if (is_trigger && !key_down) {
494 should_deactivate = true;
495 active_override_trigger_is_down = false;
496 key_override_printf("Deactivating override because trigger key up\n");
497 }
498
499 // Check if another key was pressed
500 if (key_down && (active_override->options & ko_option_no_unregister_on_other_key_down) == 0) {
501 should_deactivate = true;
502 key_override_printf("Deactivating override because another key was pressed\n");
503 }
504
505 if (should_deactivate) {
506 clear_active_override(false);
507 }
508 }
509 }
510
511#ifdef BENCH_KEY_OVERRIDE
512 uint16_t elapsed = timer_elapsed(start);
513
514 dprintf("Processing key overrides took: %u ms\n", elapsed);
515#endif
516
517 return send_key_action;
518}
diff --git a/quantum/process_keycode/process_key_override.h b/quantum/process_keycode/process_key_override.h
new file mode 100644
index 000000000..fd76f297a
--- /dev/null
+++ b/quantum/process_keycode/process_key_override.h
@@ -0,0 +1,153 @@
1/*
2 * Copyright 2021 Jonas Gessner
3 *
4 * This program is free software: you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation, either version 2 of the License, or
7 * (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program. If not, see <http://www.gnu.org/licenses/>.
16 */
17
18#pragma once
19
20#include <stdbool.h>
21#include <stddef.h>
22#include <stdint.h>
23
24#include "action_layer.h"
25
26/**
27 * Key overrides allow you to send a different key-modifier combination or perform a custom action when a certain modifier-key combination is pressed.
28 *
29 * For example, you may configure a key override to send the delete key when shift + backspace are pressed together, or that your volume keys become screen brightness keys when holding ctrl. The possibilities are quite vast and the documentation contains a few examples for inspiration.
30 *
31 * See the documentation and examples here: https://docs.qmk.fm/#/feature_key_overrides
32 */
33
34/** Bitfield with various options controlling the behavior of a key override. */
35typedef enum {
36 /** Allow activating when the trigger key is pressed down. */
37 ko_option_activation_trigger_down = (1 << 0),
38 /** Allow activating when a necessary modifier is pressed down. */
39 ko_option_activation_required_mod_down = (1 << 1),
40 /** Allow activating when a negative modifier is released. */
41 ko_option_activation_negative_mod_up = (1 << 2),
42
43 ko_options_all_activations = ko_option_activation_negative_mod_up | ko_option_activation_required_mod_down | ko_option_activation_trigger_down,
44
45 /** If set, any of the modifiers in trigger_mods will be enough to activate the override (logical OR of modifiers). If not set, all the modifiers in trigger_mods have to be pressed (logical AND of modifiers). */
46 ko_option_one_mod = (1 << 3),
47
48 /** If set, the trigger key will never be registered again after the override is deactivated. */
49 ko_option_no_reregister_trigger = (1 << 4),
50
51 /** If set, the override will not deactivate when another key is pressed down. Use only if you really know you need this. */
52 ko_option_no_unregister_on_other_key_down = (1 << 5),
53
54 /** The default options used by the ko_make_xxx functions. */
55 ko_options_default = ko_options_all_activations,
56} ko_option_t;
57
58/** Defines a single key override */
59typedef struct {
60 // The non-modifier keycode that triggers the override. This keycode, and the necessary modifiers (trigger_mods) must be pressed to activate this override. Set this to the keycode of the key that should activate the override. Set to KC_NO to require only the necessary modifiers to be pressed and no non-modifier.
61 uint16_t trigger;
62
63 // Which mods need to be down for activation. If both sides of a modifier are set (e.g. left ctrl and right ctrl) then only one is required to be pressed (e.g. left ctrl suffices). Use the MOD_MASK_XXX and MOD_BIT() macros for this.
64 uint8_t trigger_mods;
65
66 // This is a BITMASK (!), defining which layers this override applies to. To use this override on layer i set the ith bit (1 << i).
67 layer_state_t layers;
68
69 // Which modifiers cannot be down. It must hold that (active_mods & negative_mod_mask) == 0, otherwise the key override will not be activated. An active override will be deactivated once this is no longer true.
70 uint8_t negative_mod_mask;
71
72 // Modifiers to 'suppress' while the override is active. To suppress a modifier means that even though the modifier key is held down, the host OS sees the modifier as not pressed. Can be used to suppress the trigger modifiers, as a trivial example.
73 uint8_t suppressed_mods;
74
75 // The complex keycode to send as replacement when this override is triggered. This can be a simple keycode, a key-modifier combination (e.g. C(KC_A)), or KC_NO (to register no replacement keycode). Use in combination with suppressed_mods to get the correct modifiers to be sent.
76 uint16_t replacement;
77
78 // Options controlling the behavior of the override, such as what actions are allowed to activate the override.
79 ko_option_t options;
80
81 // If not NULL, this function will be called right before the replacement key is registered, along with the provided context and a flag indicating whether the override was activated or deactivated. This function allows you to run some custom actions for specific key overrides. If you return `false`, the replacement key is not registered/unregistered as it would normally. Return `true` to register and unregister the override normally.
82 bool (*custom_action)(bool activated, void *context);
83
84 // A context that will be passed to the custom action function.
85 void *context;
86
87 // If this points to false this override will not be used. Set to NULL to always have this override enabled.
88 bool *enabled;
89} key_override_t;
90
91/** Define this as a null-terminated array of pointers to key overrides. These key overrides will be used by qmk. */
92extern const key_override_t **key_overrides;
93
94/** Turns key overrides on */
95void key_override_on(void);
96
97/** Turns key overrides off */
98void key_override_off(void);
99
100/** Toggles key overrides on */
101void key_override_toggle(void);
102
103/** Returns whether key overrides are enabled */
104bool key_override_is_enabled(void);
105
106/** Handling of key overrides and its implemented keycodes */
107bool process_key_override(const uint16_t keycode, const keyrecord_t *const record);
108
109/** Perform any deferred keys */
110void key_override_task(void);
111
112/**
113 * Preferrably use these macros to create key overrides. They fix many of the options to a standard setting that should satisfy most basic use-cases. Only directly create a key_override_t struct when you really need to.
114 */
115
116// clang-format off
117
118/**
119 * Convenience initializer to create a basic key override. Activates the override on all layers.
120 */
121#define ko_make_basic(trigger_mods, trigger_key, replacement_key) \
122 ko_make_with_layers(trigger_mods, trigger_key, replacement_key, ~0)
123
124/**
125 * Convenience initializer to create a basic key override. Provide a bitmap (of type layer_state_t) with the bits set for each layer on which the override should activate.
126 */
127#define ko_make_with_layers(trigger_mods, trigger_key, replacement_key, layers) \
128 ko_make_with_layers_and_negmods(trigger_mods, trigger_key, replacement_key, layers, 0)
129
130/**
131 * Convenience initializer to create a basic key override. Provide a bitmap with the bits set for each layer on which the override should activate. Also provide a negative modifier mask, that is used to define which modifiers may not be pressed.
132 */
133#define ko_make_with_layers_and_negmods(trigger_mods, trigger_key, replacement_key, layers, negative_mask) \
134 ko_make_with_layers_negmods_and_options(trigger_mods, trigger_key, replacement_key, layers, negative_mask, ko_options_default)
135
136 /**
137 * Convenience initializer to create a basic key override. Provide a bitmap with the bits set for each layer on which the override should activate. Also provide a negative modifier mask, that is used to define which modifiers may not be pressed. Provide options for additional control of the behavior of the override.
138 */
139#define ko_make_with_layers_negmods_and_options(trigger_mods_, trigger_key, replacement_key, layer_mask, negative_mask, options_) \
140 ((const key_override_t){ \
141 .trigger_mods = (trigger_mods_), \
142 .layers = (layer_mask), \
143 .suppressed_mods = (trigger_mods_), \
144 .options = (options_), \
145 .negative_mod_mask = (negative_mask), \
146 .custom_action = NULL, \
147 .context = NULL, \
148 .trigger = (trigger_key), \
149 .replacement = (replacement_key), \
150 .enabled = NULL \
151 })
152
153// clang-format on
diff --git a/quantum/process_keycode/process_magic.c b/quantum/process_keycode/process_magic.c
index 44dd5f057..01f2fb928 100644
--- a/quantum/process_keycode/process_magic.c
+++ b/quantum/process_keycode/process_magic.c
@@ -164,6 +164,9 @@ bool process_magic(uint16_t keycode, keyrecord_t *record) {
164 case MAGIC_EE_HANDS_RIGHT: 164 case MAGIC_EE_HANDS_RIGHT:
165 eeconfig_update_handedness(false); 165 eeconfig_update_handedness(false);
166 break; 166 break;
167 case MAGIC_TOGGLE_GUI:
168 keymap_config.no_gui = !keymap_config.no_gui;
169 break;
167 } 170 }
168 171
169 eeconfig_update_keymap(keymap_config.raw); 172 eeconfig_update_keymap(keymap_config.raw);
diff --git a/quantum/process_keycode/process_music.c b/quantum/process_keycode/process_music.c
index eb06be96c..2beccbd8f 100644
--- a/quantum/process_keycode/process_music.c
+++ b/quantum/process_keycode/process_music.c
@@ -296,7 +296,7 @@ void music_mode_cycle(void) {
296# endif 296# endif
297} 297}
298 298
299void matrix_scan_music(void) { 299void music_task(void) {
300 if (music_sequence_playing) { 300 if (music_sequence_playing) {
301 if ((music_sequence_timer == 0) || (timer_elapsed(music_sequence_timer) > music_sequence_interval)) { 301 if ((music_sequence_timer == 0) || (timer_elapsed(music_sequence_timer) > music_sequence_interval)) {
302 music_sequence_timer = timer_read(); 302 music_sequence_timer = timer_read();
diff --git a/quantum/process_keycode/process_music.h b/quantum/process_keycode/process_music.h
index 01014aa6c..e275cd9d2 100644
--- a/quantum/process_keycode/process_music.h
+++ b/quantum/process_keycode/process_music.h
@@ -44,7 +44,7 @@ void music_scale_user(void);
44void music_all_notes_off(void); 44void music_all_notes_off(void);
45void music_mode_cycle(void); 45void music_mode_cycle(void);
46 46
47void matrix_scan_music(void); 47void music_task(void);
48 48
49bool music_mask(uint16_t keycode); 49bool music_mask(uint16_t keycode);
50bool music_mask_kb(uint16_t keycode); 50bool music_mask_kb(uint16_t keycode);
diff --git a/quantum/process_keycode/process_rgb.c b/quantum/process_keycode/process_rgb.c
index 167c0c03c..69853cd5c 100644
--- a/quantum/process_keycode/process_rgb.c
+++ b/quantum/process_keycode/process_rgb.c
@@ -14,7 +14,6 @@
14 * along with this program. If not, see <http://www.gnu.org/licenses/>. 14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 */ 15 */
16#include "process_rgb.h" 16#include "process_rgb.h"
17#include "rgb.h"
18 17
19typedef void (*rgb_func_pointer)(void); 18typedef void (*rgb_func_pointer)(void);
20 19
@@ -162,7 +161,7 @@ bool process_rgb(const uint16_t keycode, const keyrecord_t *record) {
162#if defined(RGBLIGHT_ENABLE) && !defined(RGBLIGHT_DISABLE_KEYCODES) && defined(RGBLIGHT_EFFECT_BREATHING) 161#if defined(RGBLIGHT_ENABLE) && !defined(RGBLIGHT_DISABLE_KEYCODES) && defined(RGBLIGHT_EFFECT_BREATHING)
163 handleKeycodeRGBMode(RGBLIGHT_MODE_BREATHING, RGBLIGHT_MODE_BREATHING_end); 162 handleKeycodeRGBMode(RGBLIGHT_MODE_BREATHING, RGBLIGHT_MODE_BREATHING_end);
164#endif 163#endif
165#if defined(RGB_MATRIX_ENABLE) && !defined(RGB_MATRIX_DISABLE_KEYCODES) && !defined(DISABLE_RGB_MATRIX_BREATHING) 164#if defined(RGB_MATRIX_ENABLE) && !defined(RGB_MATRIX_DISABLE_KEYCODES) && defined(ENABLE_RGB_MATRIX_BREATHING)
166 rgb_matrix_mode(RGB_MATRIX_BREATHING); 165 rgb_matrix_mode(RGB_MATRIX_BREATHING);
167#endif 166#endif
168 return false; 167 return false;
@@ -170,7 +169,7 @@ bool process_rgb(const uint16_t keycode, const keyrecord_t *record) {
170#if defined(RGBLIGHT_ENABLE) && !defined(RGBLIGHT_DISABLE_KEYCODES) && defined(RGBLIGHT_EFFECT_RAINBOW_MOOD) 169#if defined(RGBLIGHT_ENABLE) && !defined(RGBLIGHT_DISABLE_KEYCODES) && defined(RGBLIGHT_EFFECT_RAINBOW_MOOD)
171 handleKeycodeRGBMode(RGBLIGHT_MODE_RAINBOW_MOOD, RGBLIGHT_MODE_RAINBOW_MOOD_end); 170 handleKeycodeRGBMode(RGBLIGHT_MODE_RAINBOW_MOOD, RGBLIGHT_MODE_RAINBOW_MOOD_end);
172#endif 171#endif
173#if defined(RGB_MATRIX_ENABLE) && !defined(RGB_MATRIX_DISABLE_KEYCODES) && !defined(DISABLE_RGB_MATRIX_CYCLE_LEFT_RIGHT) 172#if defined(RGB_MATRIX_ENABLE) && !defined(RGB_MATRIX_DISABLE_KEYCODES) && defined(ENABLE_RGB_MATRIX_CYCLE_LEFT_RIGHT)
174 rgb_matrix_mode(RGB_MATRIX_CYCLE_LEFT_RIGHT); 173 rgb_matrix_mode(RGB_MATRIX_CYCLE_LEFT_RIGHT);
175#endif 174#endif
176 return false; 175 return false;
@@ -178,7 +177,7 @@ bool process_rgb(const uint16_t keycode, const keyrecord_t *record) {
178#if defined(RGBLIGHT_ENABLE) && !defined(RGBLIGHT_DISABLE_KEYCODES) && defined(RGBLIGHT_EFFECT_RAINBOW_SWIRL) 177#if defined(RGBLIGHT_ENABLE) && !defined(RGBLIGHT_DISABLE_KEYCODES) && defined(RGBLIGHT_EFFECT_RAINBOW_SWIRL)
179 handleKeycodeRGBMode(RGBLIGHT_MODE_RAINBOW_SWIRL, RGBLIGHT_MODE_RAINBOW_SWIRL_end); 178 handleKeycodeRGBMode(RGBLIGHT_MODE_RAINBOW_SWIRL, RGBLIGHT_MODE_RAINBOW_SWIRL_end);
180#endif 179#endif
181#if defined(RGB_MATRIX_ENABLE) && !defined(RGB_MATRIX_DISABLE_KEYCODES) && !defined(DISABLE_RGB_MATRIX_CYCLE_PINWHEEL) 180#if defined(RGB_MATRIX_ENABLE) && !defined(RGB_MATRIX_DISABLE_KEYCODES) && defined(ENABLE_RGB_MATRIX_CYCLE_PINWHEEL)
182 rgb_matrix_mode(RGB_MATRIX_CYCLE_PINWHEEL); 181 rgb_matrix_mode(RGB_MATRIX_CYCLE_PINWHEEL);
183#endif 182#endif
184 return false; 183 return false;
diff --git a/quantum/process_keycode/process_steno.c b/quantum/process_keycode/process_steno.c
index 57e279f21..a964aead3 100644
--- a/quantum/process_keycode/process_steno.c
+++ b/quantum/process_keycode/process_steno.c
@@ -65,6 +65,12 @@ static steno_mode_t mode;
65 65
66static const uint8_t boltmap[64] PROGMEM = {TXB_NUL, TXB_NUM, TXB_NUM, TXB_NUM, TXB_NUM, TXB_NUM, TXB_NUM, TXB_S_L, TXB_S_L, TXB_T_L, TXB_K_L, TXB_P_L, TXB_W_L, TXB_H_L, TXB_R_L, TXB_A_L, TXB_O_L, TXB_STR, TXB_STR, TXB_NUL, TXB_NUL, TXB_NUL, TXB_STR, TXB_STR, TXB_E_R, TXB_U_R, TXB_F_R, TXB_R_R, TXB_P_R, TXB_B_R, TXB_L_R, TXB_G_R, TXB_T_R, TXB_S_R, TXB_D_R, TXB_NUM, TXB_NUM, TXB_NUM, TXB_NUM, TXB_NUM, TXB_NUM, TXB_Z_R}; 66static const uint8_t boltmap[64] PROGMEM = {TXB_NUL, TXB_NUM, TXB_NUM, TXB_NUM, TXB_NUM, TXB_NUM, TXB_NUM, TXB_S_L, TXB_S_L, TXB_T_L, TXB_K_L, TXB_P_L, TXB_W_L, TXB_H_L, TXB_R_L, TXB_A_L, TXB_O_L, TXB_STR, TXB_STR, TXB_NUL, TXB_NUL, TXB_NUL, TXB_STR, TXB_STR, TXB_E_R, TXB_U_R, TXB_F_R, TXB_R_R, TXB_P_R, TXB_B_R, TXB_L_R, TXB_G_R, TXB_T_R, TXB_S_R, TXB_D_R, TXB_NUM, TXB_NUM, TXB_NUM, TXB_NUM, TXB_NUM, TXB_NUM, TXB_Z_R};
67 67
68#ifdef STENO_COMBINEDMAP
69/* Used to look up when pressing the middle row key to combine two consonant or vowel keys */
70static const uint16_t combinedmap_first[] PROGMEM = {STN_S1, STN_TL, STN_PL, STN_HL, STN_FR, STN_PR, STN_LR, STN_TR, STN_DR, STN_A, STN_E};
71static const uint16_t combinedmap_second[] PROGMEM = {STN_S2, STN_KL, STN_WL, STN_RL, STN_RR, STN_BR, STN_GR, STN_SR, STN_ZR, STN_O, STN_U};
72#endif
73
68static void steno_clear_state(void) { 74static void steno_clear_state(void) {
69 memset(state, 0, sizeof(state)); 75 memset(state, 0, sizeof(state));
70 memset(chord, 0, sizeof(chord)); 76 memset(chord, 0, sizeof(chord));
@@ -167,6 +173,15 @@ bool process_steno(uint16_t keycode, keyrecord_t *record) {
167 } 173 }
168 return false; 174 return false;
169 175
176#ifdef STENO_COMBINEDMAP
177 case QK_STENO_COMB ... QK_STENO_COMB_MAX:
178 {
179 uint8_t result;
180 result = process_steno(combinedmap_first[keycode-QK_STENO_COMB], record);
181 result &= process_steno(combinedmap_second[keycode-QK_STENO_COMB], record);
182 return result;
183 }
184#endif
170 case STN__MIN ... STN__MAX: 185 case STN__MIN ... STN__MAX:
171 if (!process_steno_user(keycode, record)) { 186 if (!process_steno_user(keycode, record)) {
172 return false; 187 return false;
diff --git a/quantum/process_keycode/process_tap_dance.c b/quantum/process_keycode/process_tap_dance.c
index 17dc540a6..c8712d919 100644
--- a/quantum/process_keycode/process_tap_dance.c
+++ b/quantum/process_keycode/process_tap_dance.c
@@ -161,7 +161,7 @@ bool process_tap_dance(uint16_t keycode, keyrecord_t *record) {
161 return true; 161 return true;
162} 162}
163 163
164void matrix_scan_tap_dance() { 164void tap_dance_task() {
165 if (highest_td == -1) return; 165 if (highest_td == -1) return;
166 uint16_t tap_user_defined; 166 uint16_t tap_user_defined;
167 167
diff --git a/quantum/process_keycode/process_tap_dance.h b/quantum/process_keycode/process_tap_dance.h
index a013c5cab..d9ffb1e73 100644
--- a/quantum/process_keycode/process_tap_dance.h
+++ b/quantum/process_keycode/process_tap_dance.h
@@ -85,7 +85,7 @@ extern qk_tap_dance_action_t tap_dance_actions[];
85 85
86void preprocess_tap_dance(uint16_t keycode, keyrecord_t *record); 86void preprocess_tap_dance(uint16_t keycode, keyrecord_t *record);
87bool process_tap_dance(uint16_t keycode, keyrecord_t *record); 87bool process_tap_dance(uint16_t keycode, keyrecord_t *record);
88void matrix_scan_tap_dance(void); 88void tap_dance_task(void);
89void reset_tap_dance(qk_tap_dance_state_t *state); 89void reset_tap_dance(qk_tap_dance_state_t *state);
90 90
91void qk_tap_dance_pair_on_each_tap(qk_tap_dance_state_t *state, void *user_data); 91void qk_tap_dance_pair_on_each_tap(qk_tap_dance_state_t *state, void *user_data);