process_combo.c (22188B)
1 /* Copyright 2016 Jack Humbert 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 17 #include "process_combo.h" 18 #include <stddef.h> 19 #include "process_auto_shift.h" 20 #include "caps_word.h" 21 #include "timer.h" 22 #include "wait.h" 23 #include "keyboard.h" 24 #include "keymap_common.h" 25 #include "action_layer.h" 26 #include "action_tapping.h" 27 #include "action_util.h" 28 #include "keymap_introspection.h" 29 30 __attribute__((weak)) void process_combo_event(uint16_t combo_index, bool pressed) {} 31 32 #ifndef COMBO_ONLY_FROM_LAYER 33 __attribute__((weak)) uint8_t combo_ref_from_layer(uint8_t layer) { 34 return layer; 35 } 36 #endif 37 38 #ifdef COMBO_MUST_HOLD_PER_COMBO 39 __attribute__((weak)) bool get_combo_must_hold(uint16_t combo_index, combo_t *combo) { 40 return false; 41 } 42 #endif 43 44 #ifdef COMBO_MUST_TAP_PER_COMBO 45 __attribute__((weak)) bool get_combo_must_tap(uint16_t combo_index, combo_t *combo) { 46 return false; 47 } 48 #endif 49 50 #ifdef COMBO_TERM_PER_COMBO 51 __attribute__((weak)) uint16_t get_combo_term(uint16_t combo_index, combo_t *combo) { 52 return COMBO_TERM; 53 } 54 #endif 55 56 #ifdef COMBO_MUST_PRESS_IN_ORDER_PER_COMBO 57 __attribute__((weak)) bool get_combo_must_press_in_order(uint16_t combo_index, combo_t *combo) { 58 return true; 59 } 60 #endif 61 62 #ifdef COMBO_PROCESS_KEY_RELEASE 63 __attribute__((weak)) bool process_combo_key_release(uint16_t combo_index, combo_t *combo, uint8_t key_index, uint16_t keycode) { 64 return false; 65 } 66 #endif 67 68 #ifdef COMBO_PROCESS_KEY_REPRESS 69 __attribute__((weak)) bool process_combo_key_repress(uint16_t combo_index, combo_t *combo, uint8_t key_index, uint16_t keycode) { 70 return false; 71 } 72 #endif 73 74 #ifdef COMBO_SHOULD_TRIGGER 75 __attribute__((weak)) bool combo_should_trigger(uint16_t combo_index, combo_t *combo, uint16_t keycode, keyrecord_t *record) { 76 return true; 77 } 78 #endif 79 80 typedef enum { COMBO_KEY_NOT_PRESSED, COMBO_KEY_PRESSED, COMBO_KEY_REPRESSED } combo_key_action_t; 81 82 #ifndef COMBO_NO_TIMER 83 static uint16_t timer = 0; 84 #endif 85 static bool b_combo_enable = true; // defaults to enabled 86 static uint16_t longest_term = 0; 87 88 typedef struct { 89 keyrecord_t record; 90 uint16_t combo_index; 91 uint16_t keycode; 92 } queued_record_t; 93 static uint8_t key_buffer_size = 0; 94 static queued_record_t key_buffer[COMBO_KEY_BUFFER_LENGTH]; 95 96 typedef struct { 97 uint16_t combo_index; 98 } queued_combo_t; 99 static uint8_t combo_buffer_write = 0; 100 static uint8_t combo_buffer_read = 0; 101 static queued_combo_t combo_buffer[COMBO_BUFFER_LENGTH]; 102 103 #define INCREMENT_MOD(i) i = (i + 1) % COMBO_BUFFER_LENGTH 104 105 #ifndef EXTRA_SHORT_COMBOS 106 /* flags are their own elements in combo_t struct. */ 107 # define COMBO_ACTIVE(combo) (combo->active) 108 # define COMBO_DISABLED(combo) (combo->disabled) 109 # define COMBO_STATE(combo) (combo->state) 110 111 # define ACTIVATE_COMBO(combo) \ 112 do { \ 113 combo->active = true; \ 114 } while (0) 115 # define DEACTIVATE_COMBO(combo) \ 116 do { \ 117 combo->active = false; \ 118 } while (0) 119 # define DISABLE_COMBO(combo) \ 120 do { \ 121 combo->disabled = true; \ 122 } while (0) 123 # define RESET_COMBO_STATE(combo) \ 124 do { \ 125 combo->disabled = false; \ 126 combo->state = 0; \ 127 } while (0) 128 #else 129 /* flags are at the two high bits of state. */ 130 # define COMBO_ACTIVE(combo) (combo->state & 0x80) 131 # define COMBO_DISABLED(combo) (combo->state & 0x40) 132 # define COMBO_STATE(combo) (combo->state & 0x3F) 133 134 # define ACTIVATE_COMBO(combo) \ 135 do { \ 136 combo->state |= 0x80; \ 137 } while (0) 138 # define DEACTIVATE_COMBO(combo) \ 139 do { \ 140 combo->state &= ~0x80; \ 141 } while (0) 142 # define DISABLE_COMBO(combo) \ 143 do { \ 144 combo->state |= 0x40; \ 145 } while (0) 146 # define RESET_COMBO_STATE(combo) \ 147 do { \ 148 combo->state &= ~0x7F; \ 149 } while (0) 150 #endif 151 152 static inline void release_combo(uint16_t combo_index, combo_t *combo) { 153 if (combo->keycode) { 154 keyrecord_t record = { 155 .event = MAKE_COMBOEVENT(false), 156 .keycode = combo->keycode, 157 }; 158 #ifndef NO_ACTION_TAPPING 159 action_tapping_process(record); 160 #else 161 process_record(&record); 162 #endif 163 } else { 164 process_combo_event(combo_index, false); 165 } 166 DEACTIVATE_COMBO(combo); 167 } 168 169 static inline bool _get_combo_must_hold(uint16_t combo_index, combo_t *combo) { 170 #ifdef COMBO_NO_TIMER 171 return false; 172 #elif defined(COMBO_MUST_HOLD_PER_COMBO) 173 return get_combo_must_hold(combo_index, combo); 174 #elif defined(COMBO_MUST_HOLD_MODS) 175 return (KEYCODE_IS_MOD(combo->keycode) || (combo->keycode >= QK_MOMENTARY && combo->keycode <= QK_MOMENTARY_MAX)); 176 #endif 177 return false; 178 } 179 180 static inline uint16_t _get_wait_time(uint16_t combo_index, combo_t *combo) { 181 if (_get_combo_must_hold(combo_index, combo) 182 #ifdef COMBO_MUST_TAP_PER_COMBO 183 || get_combo_must_tap(combo_index, combo) 184 #endif 185 ) { 186 if (longest_term < COMBO_HOLD_TERM) { 187 return COMBO_HOLD_TERM; 188 } 189 } 190 191 return longest_term; 192 } 193 194 static inline uint16_t _get_combo_term(uint16_t combo_index, combo_t *combo) { 195 #if defined(COMBO_TERM_PER_COMBO) 196 return get_combo_term(combo_index, combo); 197 #endif 198 199 return COMBO_TERM; 200 } 201 202 void clear_combos(void) { 203 uint16_t index = 0; 204 longest_term = 0; 205 for (index = 0; index < combo_count(); ++index) { 206 combo_t *combo = combo_get(index); 207 if (!COMBO_ACTIVE(combo)) { 208 RESET_COMBO_STATE(combo); 209 } 210 } 211 } 212 213 static inline void dump_key_buffer(void) { 214 /* First call start from 0 index; recursive calls need to start from i+1 index */ 215 static uint8_t key_buffer_next = 0; 216 #if TAP_CODE_DELAY > 0 217 bool delay_done = false; 218 #endif 219 220 if (key_buffer_size == 0) { 221 return; 222 } 223 224 for (uint8_t key_buffer_i = key_buffer_next; key_buffer_i < key_buffer_size; key_buffer_i++) { 225 key_buffer_next = key_buffer_i + 1; 226 227 queued_record_t *qrecord = &key_buffer[key_buffer_i]; 228 keyrecord_t *record = &qrecord->record; 229 230 if (IS_NOEVENT(record->event)) { 231 continue; 232 } 233 234 if (!record->keycode && qrecord->combo_index != (uint16_t)-1) { 235 process_combo_event(qrecord->combo_index, true); 236 } else { 237 #ifndef NO_ACTION_TAPPING 238 action_tapping_process(*record); 239 #else 240 process_record(record); 241 #endif 242 } 243 record->event.type = TICK_EVENT; 244 245 #if defined(CAPS_WORD_ENABLE) && defined(AUTO_SHIFT_ENABLE) 246 // Edge case: preserve the weak Left Shift mod if both Caps Word and 247 // Auto Shift are on. Caps Word capitalizes by setting the weak Left 248 // Shift mod during the press event, but Auto Shift doesn't send the 249 // key until it receives the release event. 250 del_weak_mods((is_caps_word_on() && get_autoshift_state()) ? ~MOD_BIT(KC_LSFT) : 0xff); 251 #else 252 clear_weak_mods(); 253 #endif // defined(CAPS_WORD_ENABLE) && defined(AUTO_SHIFT_ENABLE) 254 255 #if TAP_CODE_DELAY > 0 256 // only delay once and for a non-tapping key 257 if (!delay_done && !is_tap_record(record)) { 258 delay_done = true; 259 wait_ms(TAP_CODE_DELAY); 260 } 261 #endif 262 } 263 264 key_buffer_next = key_buffer_size = 0; 265 } 266 267 #define NO_COMBO_KEYS_ARE_DOWN (0 == COMBO_STATE(combo)) 268 #define ALL_COMBO_KEYS_ARE_DOWN(state, key_count) (((1 << key_count) - 1) == state) 269 #define ONLY_ONE_KEY_IS_DOWN(state) !(state & (state - 1)) 270 #define KEY_NOT_YET_RELEASED(state, key_index) ((1 << key_index) & state) 271 #define KEY_STATE_DOWN(state, key_index) \ 272 do { \ 273 state |= (1 << key_index); \ 274 } while (0) 275 #define KEY_STATE_UP(state, key_index) \ 276 do { \ 277 state &= ~(1 << key_index); \ 278 } while (0) 279 280 static inline void _find_key_index_and_count(const uint16_t *keys, uint16_t keycode, uint16_t *key_index, uint8_t *key_count) { 281 while (true) { 282 uint16_t key = pgm_read_word(&keys[*key_count]); 283 if (keycode == key) *key_index = *key_count; 284 if (COMBO_END == key) break; 285 (*key_count)++; 286 } 287 } 288 289 void drop_combo_from_buffer(uint16_t combo_index) { 290 /* Mark a combo as processed from the buffer. If the buffer is in the 291 * beginning of the buffer, drop it. */ 292 uint8_t i = combo_buffer_read; 293 while (i != combo_buffer_write) { 294 queued_combo_t *qcombo = &combo_buffer[i]; 295 296 if (qcombo->combo_index == combo_index) { 297 combo_t *combo = combo_get(combo_index); 298 DISABLE_COMBO(combo); 299 300 if (i == combo_buffer_read) { 301 INCREMENT_MOD(combo_buffer_read); 302 } 303 break; 304 } 305 INCREMENT_MOD(i); 306 } 307 } 308 309 void apply_combo(uint16_t combo_index, combo_t *combo) { 310 /* Apply combo's result keycode to the last chord key of the combo and 311 * disable the other keys. */ 312 313 if (COMBO_DISABLED(combo)) { 314 return; 315 } 316 317 // state to check against so we find the last key of the combo from the buffer 318 #if defined(EXTRA_EXTRA_LONG_COMBOS) 319 uint32_t state = 0; 320 #elif defined(EXTRA_LONG_COMBOS) 321 uint16_t state = 0; 322 #else 323 uint8_t state = 0; 324 #endif 325 326 for (uint8_t key_buffer_i = 0; key_buffer_i < key_buffer_size; key_buffer_i++) { 327 queued_record_t *qrecord = &key_buffer[key_buffer_i]; 328 keyrecord_t *record = &qrecord->record; 329 uint16_t keycode = qrecord->keycode; 330 331 uint8_t key_count = 0; 332 uint16_t key_index = -1; 333 _find_key_index_and_count(combo->keys, keycode, &key_index, &key_count); 334 335 if (-1 == (int16_t)key_index) { 336 // key not part of this combo 337 continue; 338 } 339 340 KEY_STATE_DOWN(state, key_index); 341 if (ALL_COMBO_KEYS_ARE_DOWN(state, key_count)) { 342 // this in the end executes the combo when the key_buffer is dumped. 343 record->keycode = combo->keycode; 344 record->event.type = COMBO_EVENT; 345 record->event.key = MAKE_KEYPOS(0, 0); 346 347 qrecord->combo_index = combo_index; 348 ACTIVATE_COMBO(combo); 349 350 if (key_count == 1) { 351 release_combo(combo_index, combo); 352 } 353 354 break; 355 } else { 356 // key was part of the combo but not the last one, "disable" it 357 // by making it a TICK event. 358 record->event.type = TICK_EVENT; 359 } 360 } 361 drop_combo_from_buffer(combo_index); 362 } 363 364 static inline void apply_combos(void) { 365 // Apply all buffered normal combos. 366 for (uint8_t i = combo_buffer_read; i != combo_buffer_write; INCREMENT_MOD(i)) { 367 queued_combo_t *buffered_combo = &combo_buffer[i]; 368 combo_t *combo = combo_get(buffered_combo->combo_index); 369 370 #ifdef COMBO_MUST_TAP_PER_COMBO 371 if (get_combo_must_tap(buffered_combo->combo_index, combo)) { 372 // Tap-only combos are applied on key release only, so let's drop 'em here. 373 drop_combo_from_buffer(buffered_combo->combo_index); 374 continue; 375 } 376 #endif 377 apply_combo(buffered_combo->combo_index, combo); 378 } 379 dump_key_buffer(); 380 clear_combos(); 381 } 382 383 combo_t *overlaps(combo_t *combo1, combo_t *combo2) { 384 /* Checks if the combos overlap and returns the combo that should be 385 * dropped from the combo buffer. 386 * The combo that has less keys will be dropped. If they have the same 387 * amount of keys, drop combo1. */ 388 389 uint8_t idx1 = 0, idx2 = 0; 390 uint16_t key1, key2; 391 bool overlaps = false; 392 393 while ((key1 = pgm_read_word(&combo1->keys[idx1])) != COMBO_END) { 394 idx2 = 0; 395 while ((key2 = pgm_read_word(&combo2->keys[idx2])) != COMBO_END) { 396 if (key1 == key2) overlaps = true; 397 idx2 += 1; 398 } 399 idx1 += 1; 400 } 401 402 if (!overlaps) return NULL; 403 if (idx2 < idx1) return combo2; 404 return combo1; 405 } 406 407 #if defined(COMBO_MUST_PRESS_IN_ORDER) || defined(COMBO_MUST_PRESS_IN_ORDER_PER_COMBO) 408 static bool keys_pressed_in_order(uint16_t combo_index, combo_t *combo, uint16_t key_index, uint16_t keycode, keyrecord_t *record) { 409 # ifdef COMBO_MUST_PRESS_IN_ORDER_PER_COMBO 410 if (!get_combo_must_press_in_order(combo_index, combo)) { 411 return true; 412 } 413 # endif 414 if ( 415 // The `state` bit for the key being pressed. 416 (1 << key_index) == 417 // The *next* combo key's bit. 418 (COMBO_STATE(combo) + 1) 419 // E.g. two keys already pressed: `state == 11`. 420 // Next possible `state` is `111`. 421 // So the needed bit is `100` which we get with `11 + 1`. 422 ) { 423 return true; 424 } 425 return false; 426 } 427 #endif 428 429 static combo_key_action_t process_single_combo(combo_t *combo, uint16_t keycode, keyrecord_t *record, uint16_t combo_index) { 430 uint8_t key_count = 0; 431 uint16_t key_index = -1; 432 _find_key_index_and_count(combo->keys, keycode, &key_index, &key_count); 433 434 /* Continue processing if key isn't part of current combo. */ 435 if (-1 == (int16_t)key_index) { 436 return COMBO_KEY_NOT_PRESSED; 437 } 438 439 bool key_is_part_of_combo = (!COMBO_DISABLED(combo) && is_combo_enabled() 440 #if defined(COMBO_MUST_PRESS_IN_ORDER) || defined(COMBO_MUST_PRESS_IN_ORDER_PER_COMBO) 441 && keys_pressed_in_order(combo_index, combo, key_index, keycode, record) 442 #endif 443 #ifdef COMBO_SHOULD_TRIGGER 444 && combo_should_trigger(combo_index, combo, keycode, record) 445 #endif 446 ); 447 448 if (record->event.pressed && key_is_part_of_combo) { 449 uint16_t time = _get_combo_term(combo_index, combo); 450 if (!COMBO_ACTIVE(combo)) { 451 KEY_STATE_DOWN(combo->state, key_index); 452 if (longest_term < time) { 453 longest_term = time; 454 } 455 } 456 if (ALL_COMBO_KEYS_ARE_DOWN(COMBO_STATE(combo), key_count)) { 457 /* Combo was fully pressed */ 458 /* Buffer the combo so we can fire it after COMBO_TERM */ 459 460 #ifndef COMBO_NO_TIMER 461 /* Don't buffer this combo if its combo term has passed. */ 462 if (timer && timer_elapsed(timer) > time) { 463 DISABLE_COMBO(combo); 464 return COMBO_KEY_PRESSED; 465 } else 466 #endif 467 { 468 469 // disable readied combos that overlap with this combo 470 combo_t *drop = NULL; 471 for (uint8_t combo_buffer_i = combo_buffer_read; combo_buffer_i != combo_buffer_write; INCREMENT_MOD(combo_buffer_i)) { 472 queued_combo_t *qcombo = &combo_buffer[combo_buffer_i]; 473 combo_t *buffered_combo = combo_get(qcombo->combo_index); 474 475 if ((drop = overlaps(buffered_combo, combo))) { 476 DISABLE_COMBO(drop); 477 if (drop == combo) { 478 // stop checking for overlaps if dropped combo was current combo. 479 break; 480 } else if (combo_buffer_i == combo_buffer_read && drop == buffered_combo) { 481 /* Drop the disabled buffered combo from the buffer if 482 * it is in the beginning of the buffer. */ 483 INCREMENT_MOD(combo_buffer_read); 484 } 485 } 486 } 487 488 if (drop != combo) { 489 // save this combo to buffer 490 combo_buffer[combo_buffer_write] = (queued_combo_t){ 491 .combo_index = combo_index, 492 }; 493 INCREMENT_MOD(combo_buffer_write); 494 495 // get possible longer waiting time for tap-/hold-only combos. 496 longest_term = _get_wait_time(combo_index, combo); 497 } 498 } // if timer elapsed end 499 } 500 #ifdef COMBO_PROCESS_KEY_REPRESS 501 } else if (record->event.pressed) { 502 if (COMBO_ACTIVE(combo)) { 503 if (process_combo_key_repress(combo_index, combo, key_index, keycode)) { 504 KEY_STATE_DOWN(combo->state, key_index); 505 return COMBO_KEY_REPRESSED; 506 } 507 } 508 #endif 509 } else { 510 // chord releases 511 if (!COMBO_ACTIVE(combo) && ALL_COMBO_KEYS_ARE_DOWN(COMBO_STATE(combo), key_count)) { 512 /* First key quickly released */ 513 if (COMBO_DISABLED(combo) || _get_combo_must_hold(combo_index, combo)) { 514 // combo wasn't tappable, disable it and drop it from buffer. 515 drop_combo_from_buffer(combo_index); 516 key_is_part_of_combo = false; 517 } 518 #ifdef COMBO_MUST_TAP_PER_COMBO 519 else if (get_combo_must_tap(combo_index, combo)) { 520 // immediately apply tap-only combo 521 apply_combo(combo_index, combo); 522 apply_combos(); // also apply other prepared combos and dump key buffer 523 # ifdef COMBO_PROCESS_KEY_RELEASE 524 if (process_combo_key_release(combo_index, combo, key_index, keycode)) { 525 release_combo(combo_index, combo); 526 } 527 # endif 528 } 529 #endif 530 } else if (COMBO_ACTIVE(combo) && ONLY_ONE_KEY_IS_DOWN(COMBO_STATE(combo)) && KEY_NOT_YET_RELEASED(COMBO_STATE(combo), key_index)) { 531 /* last key released */ 532 release_combo(combo_index, combo); 533 key_is_part_of_combo = true; 534 535 #ifdef COMBO_PROCESS_KEY_RELEASE 536 process_combo_key_release(combo_index, combo, key_index, keycode); 537 #endif 538 } else if (COMBO_ACTIVE(combo) && KEY_NOT_YET_RELEASED(COMBO_STATE(combo), key_index)) { 539 /* first or middle key released */ 540 key_is_part_of_combo = true; 541 542 #ifdef COMBO_PROCESS_KEY_RELEASE 543 if (process_combo_key_release(combo_index, combo, key_index, keycode)) { 544 release_combo(combo_index, combo); 545 } 546 #endif 547 } else { 548 /* The released key was part of an incomplete combo */ 549 key_is_part_of_combo = false; 550 } 551 552 KEY_STATE_UP(combo->state, key_index); 553 } 554 555 return key_is_part_of_combo ? COMBO_KEY_PRESSED : COMBO_KEY_NOT_PRESSED; 556 } 557 558 bool process_combo(uint16_t keycode, keyrecord_t *record) { 559 uint8_t is_combo_key = COMBO_KEY_NOT_PRESSED; 560 bool no_combo_keys_pressed = true; 561 562 if (keycode == QK_COMBO_ON && record->event.pressed) { 563 combo_enable(); 564 return true; 565 } 566 567 if (keycode == QK_COMBO_OFF && record->event.pressed) { 568 combo_disable(); 569 return true; 570 } 571 572 if (keycode == QK_COMBO_TOGGLE && record->event.pressed) { 573 combo_toggle(); 574 return true; 575 } 576 577 #ifdef COMBO_ONLY_FROM_LAYER 578 /* Only check keycodes from one layer. */ 579 keycode = keymap_key_to_keycode(COMBO_ONLY_FROM_LAYER, record->event.key); 580 #else 581 uint8_t highest_layer = get_highest_layer(layer_state | default_layer_state); 582 uint8_t ref_layer = combo_ref_from_layer(highest_layer); 583 if (ref_layer != highest_layer) { 584 keycode = keymap_key_to_keycode(ref_layer, record->event.key); 585 } 586 #endif 587 588 for (uint16_t idx = 0; idx < combo_count(); ++idx) { 589 combo_t *combo = combo_get(idx); 590 is_combo_key |= process_single_combo(combo, keycode, record, idx); 591 no_combo_keys_pressed = no_combo_keys_pressed && (NO_COMBO_KEYS_ARE_DOWN || COMBO_ACTIVE(combo) || COMBO_DISABLED(combo)); 592 } 593 594 if (record->event.pressed && is_combo_key) { 595 #ifndef COMBO_NO_TIMER 596 # ifdef COMBO_STRICT_TIMER 597 if (!timer) { 598 // timer is set only on the first key 599 timer = timer_read(); 600 } 601 # else 602 timer = timer_read(); 603 # endif 604 #endif 605 606 #ifdef COMBO_PROCESS_KEY_REPRESS 607 if (is_combo_key == COMBO_KEY_PRESSED) 608 #endif 609 { 610 if (key_buffer_size < COMBO_KEY_BUFFER_LENGTH) { 611 key_buffer[key_buffer_size++] = (queued_record_t){ 612 .record = *record, 613 .keycode = keycode, 614 .combo_index = -1, // this will be set when applying combos 615 }; 616 } 617 } 618 } else { 619 if (combo_buffer_read != combo_buffer_write) { 620 // some combo is prepared 621 apply_combos(); 622 } else { 623 // reset state if there are no combo keys pressed at all 624 dump_key_buffer(); 625 #ifndef COMBO_NO_TIMER 626 timer = 0; 627 #endif 628 clear_combos(); 629 } 630 } 631 return !is_combo_key; 632 } 633 634 void combo_task(void) { 635 if (!b_combo_enable) { 636 return; 637 } 638 639 #ifndef COMBO_NO_TIMER 640 if (timer && timer_elapsed(timer) > longest_term) { 641 if (combo_buffer_read != combo_buffer_write) { 642 apply_combos(); 643 longest_term = 0; 644 timer = 0; 645 } else { 646 dump_key_buffer(); 647 timer = 0; 648 clear_combos(); 649 } 650 } 651 #endif 652 } 653 654 void combo_enable(void) { 655 b_combo_enable = true; 656 } 657 658 void combo_disable(void) { 659 #ifndef COMBO_NO_TIMER 660 timer = 0; 661 #endif 662 b_combo_enable = false; 663 combo_buffer_read = combo_buffer_write; 664 clear_combos(); 665 dump_key_buffer(); 666 } 667 668 void combo_toggle(void) { 669 if (b_combo_enable) { 670 combo_disable(); 671 } else { 672 combo_enable(); 673 } 674 } 675 676 bool is_combo_enabled(void) { 677 return b_combo_enable; 678 }