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.c548
-rw-r--r--quantum/process_keycode/process_combo.h36
2 files changed, 476 insertions, 108 deletions
diff --git a/quantum/process_keycode/process_combo.c b/quantum/process_keycode/process_combo.c
index 9e1629248..b4e698dec 100644
--- a/quantum/process_keycode/process_combo.c
+++ b/quantum/process_keycode/process_combo.c
@@ -16,114 +16,445 @@
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
34 39
35static uint8_t buffer_size = 0; 40#ifdef COMBO_TERM_PER_COMBO
36#ifdef COMBO_ALLOW_ACTION_KEYS 41__attribute__((weak)) uint16_t get_combo_term(uint16_t index, combo_t *combo) { return COMBO_TERM; }
37static keyrecord_t key_buffer[MAX_COMBO_LENGTH]; 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
47
48#ifndef COMBO_NO_TIMER
49static uint16_t timer = 0;
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);
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;
153}
154
155void clear_combos(void) {
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 }
51 } 163 }
52} 164}
53 165
54static inline void dump_key_buffer(bool emit) { 166static inline void dump_key_buffer(void) {
55 if (buffer_size == 0) { 167 if (key_buffer_size == 0) {
56 return; 168 return;
57 } 169 }
58 170
59 if (emit) { 171 for (uint8_t key_buffer_i = 0; key_buffer_i < key_buffer_size; key_buffer_i++) {
60 for (uint8_t i = 0; i < buffer_size; i++) { 172
61#ifdef COMBO_ALLOW_ACTION_KEYS 173 queued_record_t *qrecord = &key_buffer[key_buffer_i];
62 const action_t action = store_or_get_action(key_buffer[i].event.pressed, key_buffer[i].event.key); 174 keyrecord_t *record = &qrecord->record;
63 process_action(&(key_buffer[i]), action); 175
176 if (IS_NOEVENT(record->event)) {
177 continue;
178 }
179
180 if (!record->keycode && qrecord->combo_index != (uint16_t)-1) {
181 process_combo_event(qrecord->combo_index, true);
182 } else {
183#ifndef NO_ACTION_TAPPING
184 action_tapping_process(*record);
64#else 185#else
65 register_code16(key_buffer[i]); 186 process_record(record);
66 send_keyboard_report();
67#endif 187#endif
68 } 188 }
189 record->event.time = 0;
69 } 190 }
70 191
71 buffer_size = 0; 192 key_buffer_size = 0;
72} 193}
73 194
74#define ALL_COMBO_KEYS_ARE_DOWN (((1 << count) - 1) == combo->state) 195#define NO_COMBO_KEYS_ARE_DOWN (0 == COMBO_STATE(combo))
75#define KEY_STATE_DOWN(key) \ 196#define ALL_COMBO_KEYS_ARE_DOWN(state, key_count) (((1 << key_count) - 1) == state)
76 do { \ 197#define ONLY_ONE_KEY_IS_DOWN(state) !(state & (state - 1))
77 combo->state |= (1 << key); \ 198#define KEY_NOT_YET_RELEASED(state, key_index) ((1 << key_index) & state)
199#define KEY_STATE_DOWN(state, key_index) \
200 do { \
201 state |= (1 << key_index); \
78 } while (0) 202 } while (0)
79#define KEY_STATE_UP(key) \ 203#define KEY_STATE_UP(state, key_index) \
80 do { \ 204 do { \
81 combo->state &= ~(1 << key); \ 205 state &= ~(1 << key_index); \
82 } while (0) 206 } while (0)
83 207
84static bool process_single_combo(combo_t *combo, uint16_t keycode, keyrecord_t *record) { 208static 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; 209 while (true) {
86 uint16_t index = -1; 210 uint16_t key = pgm_read_word(&keys[*key_count]);
87 /* Find index of keycode and number of combo keys */ 211 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; 212 if (COMBO_END == key) break;
213 (*key_count)++;
214 }
215}
216
217void drop_combo_from_buffer(uint16_t combo_index) {
218 /* Mark a combo as processed from the buffer. If the buffer is in the
219 * beginning of the buffer, drop it. */
220 uint8_t i = combo_buffer_read;
221 while (i != combo_buffer_write) {
222 queued_combo_t *qcombo = &combo_buffer[i];
223
224 if (qcombo->combo_index == combo_index) {
225 combo_t *combo = &key_combos[combo_index];
226 DISABLE_COMBO(combo);
227
228 if (i == combo_buffer_read) {
229 INCREMENT_MOD(combo_buffer_read);
230 }
231 break;
232 }
233 INCREMENT_MOD(i);
92 } 234 }
235}
236
237void apply_combo(uint16_t combo_index, combo_t *combo) {
238 /* Apply combo's result keycode to the last chord key of the combo and
239 * disable the other keys. */
93 240
94 /* Continue processing if not a combo key */ 241 if (COMBO_DISABLED(combo)) { return; }
95 if (-1 == (int8_t)index) return false;
96 242
97 bool is_combo_active = is_active; 243 // state to check against so we find the last key of the combo from the buffer
244#if defined(EXTRA_EXTRA_LONG_COMBOS)
245 uint32_t state = 0;
246#elif defined(EXTRA_LONG_COMBOS)
247 uint16_t state = 0;
248#else
249 uint8_t state = 0;
250#endif
251
252 for (uint8_t key_buffer_i = 0; key_buffer_i < key_buffer_size; key_buffer_i++) {
253
254 queued_record_t *qrecord = &key_buffer[key_buffer_i];
255 keyrecord_t *record = &qrecord->record;
256 uint16_t keycode = qrecord->keycode;
257
258 uint8_t key_count = 0;
259 uint16_t key_index = -1;
260 _find_key_index_and_count(combo->keys, keycode, &key_index, &key_count);
261
262 if (-1 == (int16_t)key_index) {
263 // key not part of this combo
264 continue;
265 }
98 266
99 if (record->event.pressed) { 267 KEY_STATE_DOWN(state, key_index);
100 KEY_STATE_DOWN(index); 268 if (ALL_COMBO_KEYS_ARE_DOWN(state, key_count)) {
269 // this in the end executes the combo when the key_buffer is dumped.
270 record->keycode = combo->keycode;
271 record->event.key = COMBO_KEY_POS;
101 272
102 if (is_combo_active) { 273 qrecord->combo_index = combo_index;
103 if (ALL_COMBO_KEYS_ARE_DOWN) { /* Combo was pressed */ 274 ACTIVATE_COMBO(combo);
104 send_combo(combo->keycode, true); 275
105 drop_buffer = true; 276 break;
277 } else {
278 // key was part of the combo but not the last one, "disable" it
279 // by making it a TICK event.
280 record->event.time = 0;
281 }
282
283 }
284 drop_combo_from_buffer(combo_index);
285}
286
287static inline void apply_combos(void) {
288 // Apply all buffered normal combos.
289 for (uint8_t i = combo_buffer_read;
290 i != combo_buffer_write;
291 INCREMENT_MOD(i)) {
292
293 queued_combo_t *buffered_combo = &combo_buffer[i];
294 combo_t *combo = &key_combos[buffered_combo->combo_index];
295
296#ifdef COMBO_MUST_TAP_PER_COMBO
297 if (get_combo_must_tap(buffered_combo->combo_index, combo)) {
298 // Tap-only combos are applied on key release only, so let's drop 'em here.
299 drop_combo_from_buffer(buffered_combo->combo_index);
300 continue;
301 }
302#endif
303 apply_combo(buffered_combo->combo_index, combo);
304 }
305 dump_key_buffer();
306 clear_combos();
307}
308
309combo_t* overlaps(combo_t *combo1, combo_t *combo2) {
310 /* Checks if the combos overlap and returns the combo that should be
311 * dropped from the combo buffer.
312 * The combo that has less keys will be dropped. If they have the same
313 * amount of keys, drop combo1. */
314
315 uint8_t idx1 = 0, idx2 = 0;
316 uint16_t key1, key2;
317 bool overlaps = false;
318
319 while ((key1 = pgm_read_word(&combo1->keys[idx1])) != COMBO_END) {
320 idx2 = 0;
321 while ((key2 = pgm_read_word(&combo2->keys[idx2])) != COMBO_END) {
322 if (key1 == key2) overlaps = true;
323 idx2 += 1;
324 }
325 idx1 += 1;
326 }
327
328 if (!overlaps) return NULL;
329 if (idx2 < idx1) return combo2;
330 return combo1;
331}
332
333static bool process_single_combo(combo_t *combo, uint16_t keycode, keyrecord_t *record, uint16_t combo_index) {
334 uint8_t key_count = 0;
335 uint16_t key_index = -1;
336 _find_key_index_and_count(combo->keys, keycode, &key_index, &key_count);
337
338 /* Continue processing if key isn't part of current combo. */
339 if (-1 == (int16_t)key_index) {
340 return false;
341 }
342
343 bool key_is_part_of_combo = !COMBO_DISABLED(combo);
344
345 if (record->event.pressed && !COMBO_DISABLED(combo)) {
346 uint16_t time = _get_combo_term(combo_index, combo);
347 if (!COMBO_ACTIVE(combo)) {
348 KEY_STATE_DOWN(combo->state, key_index);
349 if (longest_term < time) {
350 longest_term = time;
106 } 351 }
107 } 352 }
353 if (ALL_COMBO_KEYS_ARE_DOWN(COMBO_STATE(combo), key_count)) {
354 /* Combo was fully pressed */
355 /* Buffer the combo so we can fire it after COMBO_TERM */
356
357#ifndef COMBO_NO_TIMER
358 /* Don't buffer this combo if its combo term has passed. */
359 if (timer && timer_elapsed(timer) > time) {
360 DISABLE_COMBO(combo);
361 return true;
362 } else
363#endif
364 {
365
366 // disable readied combos that overlap with this combo
367 combo_t *drop = NULL;
368 for (uint8_t combo_buffer_i = combo_buffer_read;
369 combo_buffer_i != combo_buffer_write;
370 INCREMENT_MOD(combo_buffer_i)) {
371
372 queued_combo_t *qcombo = &combo_buffer[combo_buffer_i];
373 combo_t *buffered_combo = &key_combos[qcombo->combo_index];
374
375 if ((drop = overlaps(buffered_combo, combo))) {
376 DISABLE_COMBO(drop);
377 if (drop == combo) {
378 // stop checking for overlaps if dropped combo was current combo.
379 break;
380 } else if (combo_buffer_i == combo_buffer_read && drop == buffered_combo) {
381 /* Drop the disabled buffered combo from the buffer if
382 * it is in the beginning of the buffer. */
383 INCREMENT_MOD(combo_buffer_read);
384 }
385 }
386
387 }
388
389 if (drop != combo) {
390 // save this combo to buffer
391 combo_buffer[combo_buffer_write] = (queued_combo_t){
392 .combo_index=combo_index,
393 };
394 INCREMENT_MOD(combo_buffer_write);
395
396 // get possible longer waiting time for tap-/hold-only combos.
397 longest_term = _get_wait_time(combo_index, combo);
398 }
399 } // if timer elapsed end
400
401 }
108 } else { 402 } else {
109 if (ALL_COMBO_KEYS_ARE_DOWN) { /* Combo was released */ 403 // chord releases
110 send_combo(combo->keycode, false); 404 if (!COMBO_ACTIVE(combo) && ALL_COMBO_KEYS_ARE_DOWN(COMBO_STATE(combo), key_count)) {
405 /* First key quickly released */
406 if (COMBO_DISABLED(combo) || _get_combo_must_hold(combo_index, combo)) {
407 // combo wasn't tappable, disable it and drop it from buffer.
408 drop_combo_from_buffer(combo_index);
409 key_is_part_of_combo = false;
410 }
411#ifdef COMBO_MUST_TAP_PER_COMBO
412 else if (get_combo_must_tap(combo_index, combo)) {
413 // immediately apply tap-only combo
414 apply_combo(combo_index, combo);
415 apply_combos(); // also apply other prepared combos and dump key buffer
416# ifdef COMBO_PROCESS_KEY_RELEASE
417 if (process_combo_key_release(combo_index, combo, key_index, keycode)) {
418 release_combo(combo_index, combo);
419 }
420# endif
421 }
422#endif
423 } else if (COMBO_ACTIVE(combo)
424 && ONLY_ONE_KEY_IS_DOWN(COMBO_STATE(combo))
425 && KEY_NOT_YET_RELEASED(COMBO_STATE(combo), key_index)
426 ) {
427 /* last key released */
428 release_combo(combo_index, combo);
429 key_is_part_of_combo = true;
430
431#ifdef COMBO_PROCESS_KEY_RELEASE
432 process_combo_key_release(combo_index, combo, key_index, keycode);
433#endif
434 } else if (COMBO_ACTIVE(combo)
435 && KEY_NOT_YET_RELEASED(COMBO_STATE(combo), key_index)
436 ) {
437 /* first or middle key released */
438 key_is_part_of_combo = true;
439
440#ifdef COMBO_PROCESS_KEY_RELEASE
441 if (process_combo_key_release(combo_index, combo, key_index, keycode)) {
442 release_combo(combo_index, combo);
443 }
444#endif
111 } else { 445 } else {
112 /* continue processing without immediately returning */ 446 /* The released key was part of an incomplete combo */
113 is_combo_active = false; 447 key_is_part_of_combo = false;
114 } 448 }
115 449
116 KEY_STATE_UP(index); 450 KEY_STATE_UP(combo->state, key_index);
117 } 451 }
118 452
119 return is_combo_active; 453 return key_is_part_of_combo;
120} 454}
121 455
122#define NO_COMBO_KEYS_ARE_DOWN (0 == combo->state)
123
124bool process_combo(uint16_t keycode, keyrecord_t *record) { 456bool process_combo(uint16_t keycode, keyrecord_t *record) {
125 bool is_combo_key = false; 457 bool is_combo_key = false;
126 drop_buffer = false;
127 bool no_combo_keys_pressed = true; 458 bool no_combo_keys_pressed = true;
128 459
129 if (keycode == CMB_ON && record->event.pressed) { 460 if (keycode == CMB_ON && record->event.pressed) {
@@ -144,62 +475,81 @@ bool process_combo(uint16_t keycode, keyrecord_t *record) {
144 if (!is_combo_enabled()) { 475 if (!is_combo_enabled()) {
145 return true; 476 return true;
146 } 477 }
147#ifndef COMBO_VARIABLE_LEN 478
148 for (current_combo_index = 0; current_combo_index < COMBO_COUNT; ++current_combo_index) { 479#ifdef COMBO_ONLY_FROM_LAYER
149#else 480 /* Only check keycodes from one layer. */
150 for (current_combo_index = 0; current_combo_index < COMBO_LEN; ++current_combo_index) { 481 keycode = keymap_key_to_keycode(COMBO_ONLY_FROM_LAYER, record->event.key);
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 combo_task(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;
203} 553}
204 554
205void combo_toggle(void) { 555void combo_toggle(void) {
diff --git a/quantum/process_keycode/process_combo.h b/quantum/process_keycode/process_combo.h
index 9af97588b..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,12 +61,15 @@ 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 combo_task(void); 75void combo_task(void);