qmk_firmware

QMK firmware for my keyboards (Corne, Sweep Ferris) and trackball (Ploopy Adept)
Log | Files | Refs | Submodules | LICENSE

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 }