aboutsummaryrefslogtreecommitdiff
path: root/quantum/process_keycode/process_combo.c
diff options
context:
space:
mode:
Diffstat (limited to 'quantum/process_keycode/process_combo.c')
-rw-r--r--quantum/process_keycode/process_combo.c558
1 files changed, 455 insertions, 103 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) {