qmk_firmware

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

combo.md (19833B)


      1 # Combos
      2 
      3 The Combo feature is a chording type solution for adding custom actions. It lets you hit multiple keys at once and produce a different effect. For instance, hitting `A` and `B` within the combo term would hit `ESC` instead, or have it perform even more complex tasks.
      4 
      5 To enable this feature, you need to add `COMBO_ENABLE = yes` to your `rules.mk`.
      6 
      7 Then, in your `keymap.c` file, you'll need to define a sequence of keys, terminated with `COMBO_END`, and a structure to list the combination of keys, and its resulting action.
      8 
      9 ```c
     10 const uint16_t PROGMEM test_combo1[] = {KC_A, KC_B, COMBO_END};
     11 const uint16_t PROGMEM test_combo2[] = {KC_C, KC_D, COMBO_END};
     12 combo_t key_combos[] = {
     13     COMBO(test_combo1, KC_ESC),
     14     COMBO(test_combo2, LCTL(KC_Z)), // keycodes with modifiers are possible too!
     15 };
     16 ```
     17 
     18 This will send "Escape" if you hit the A and B keys, and Ctrl+Z when you hit the C and D keys.
     19 
     20 ## Advanced Keycodes Support
     21 Advanced keycodes, such as [Mod-Tap](../mod_tap) and [Tap Dance](tap_dance) are also supported together with combos. If you use these advanced keycodes in your keymap, you will need to place the full keycode in the combo definition, e.g.:
     22 
     23 ```c
     24 const uint16_t PROGMEM test_combo1[] = {LSFT_T(KC_A), LT(1, KC_B), COMBO_END};
     25 const uint16_t PROGMEM test_combo2[] = {TD(TD_ESC_CAPS), KC_F1, COMBO_END};
     26 ```
     27 
     28 ## Overlapping Combos
     29 It is possible to overlap combos. Before, with the example below both combos would activate when all three keys were pressed. Now only the three key combo will activate.
     30 
     31 ```c
     32 const uint16_t PROGMEM test_combo1[] = {LSFT_T(KC_A), LT(1, KC_B), COMBO_END};
     33 const uint16_t PROGMEM test_combo2[] = {LSFT_T(KC_A), LT(1, KC_B), KC_C, COMBO_END};
     34 combo_t key_combos[] = {
     35     COMBO(test_combo1, KC_ESC)
     36     COMBO(test_combo2, KC_TAB)
     37 };
     38 ```
     39 
     40 ## Examples
     41 
     42 A long list of combos can be defined in an `enum` list:
     43 
     44 ```c
     45 enum combos {
     46   AB_ESC,
     47   JK_TAB,
     48   QW_SFT,
     49   SD_LAYER
     50 };
     51 
     52 const uint16_t PROGMEM ab_combo[] = {KC_A, KC_B, COMBO_END};
     53 const uint16_t PROGMEM jk_combo[] = {KC_J, KC_K, COMBO_END};
     54 const uint16_t PROGMEM qw_combo[] = {KC_Q, KC_W, COMBO_END};
     55 const uint16_t PROGMEM sd_combo[] = {KC_S, KC_D, COMBO_END};
     56 
     57 combo_t key_combos[] = {
     58   [AB_ESC] = COMBO(ab_combo, KC_ESC),
     59   [JK_TAB] = COMBO(jk_combo, KC_TAB),
     60   [QW_SFT] = COMBO(qw_combo, KC_LSFT),
     61   [SD_LAYER] = COMBO(sd_combo, MO(_LAYER)),
     62 };
     63 ```
     64 
     65 For a more complicated implementation, you can use the `process_combo_event` function to add custom handling.
     66 
     67 ```c
     68 enum combo_events {
     69   EM_EMAIL,
     70   BSPC_LSFT_CLEAR,
     71 };
     72 
     73 const uint16_t PROGMEM email_combo[] = {KC_E, KC_M, COMBO_END};
     74 const uint16_t PROGMEM clear_line_combo[] = {KC_BSPC, KC_LSFT, COMBO_END};
     75 
     76 combo_t key_combos[] = {
     77   [EM_EMAIL] = COMBO_ACTION(email_combo),
     78   [BSPC_LSFT_CLEAR] = COMBO_ACTION(clear_line_combo),
     79 };
     80 /* COMBO_ACTION(x) is same as COMBO(x, KC_NO) */
     81 
     82 void process_combo_event(uint16_t combo_index, bool pressed) {
     83   switch(combo_index) {
     84     case EM_EMAIL:
     85       if (pressed) {
     86         SEND_STRING("john.doe@example.com");
     87       }
     88       break;
     89     case BSPC_LSFT_CLEAR:
     90       if (pressed) {
     91         tap_code16(KC_END);
     92         tap_code16(S(KC_HOME));
     93         tap_code16(KC_BSPC);
     94       }
     95       break;
     96   }
     97 }
     98 ```
     99 
    100 This will send "john.doe@example.com" if you chord E and M together, and clear the current line with Backspace and Left-Shift. You could change this to do stuff like play sounds or change settings.
    101 
    102 It is worth noting that `COMBO_ACTION`s are not needed anymore. As of [PR#8591](https://github.com/qmk/qmk_firmware/pull/8591/), it is possible to run your own custom keycodes from combos. Just define the custom keycode, program its functionality in `process_record_user`, and define a combo with `COMBO(<key_array>, <your_custom_keycode>)`. See the first example in [Macros](../feature_macros).
    103 
    104 ## Keycodes
    105 You can enable, disable and toggle the Combo feature on the fly. This is useful if you need to disable them temporarily, such as for a game. The following keycodes are available for use in your `keymap.c`
    106 
    107 |Keycode          |Aliases  |Description                     |
    108 |-----------------|---------|--------------------------------|
    109 |`QK_COMBO_ON`    |`CM_ON`  |Turns on Combo feature          |
    110 |`QK_COMBO_OFF`   |`CM_OFF` |Turns off Combo feature         |
    111 |`QK_COMBO_TOGGLE`|`CM_TOGG`|Toggles Combo feature on and off|
    112 
    113 ## Advanced Configuration
    114 These configuration settings can be set in your `config.h` file.
    115 
    116 ### Combo Term
    117 By default, the timeout for the Combos to be recognized is set to 50ms. This can be changed if accidental combo misfires are happening or if you're having difficulties pressing keys at the same time. For instance, `#define COMBO_TERM 40` would set the timeout period for combos to 40ms.
    118 
    119 ### Buffer and state sizes
    120 If you're using long combos, or you have a lot of overlapping combos, you may run into issues with this, as the buffers may not be large enough to accommodate what you're doing. In this case, you can configure the sizes of the buffers used. Be aware, larger combo sizes and larger buffers will increase memory usage!
    121 
    122 To configure the amount of keys a combo can be composed of, change the following:
    123 
    124 | Keys | Define to be set                  |
    125 |------|-----------------------------------|
    126 | 6    | `#define EXTRA_SHORT_COMBOS`      |
    127 | 8    | QMK Default                       |
    128 | 16   | `#define EXTRA_LONG_COMBOS`       |
    129 | 32   | `#define EXTRA_EXTRA_LONG_COMBOS` |
    130 
    131 Defining `EXTRA_SHORT_COMBOS` combines a combo's internal state into just one byte. This can, in some cases, save some memory. If it doesn't, no point using it. If you do, you also have to make sure you don't define combos with more than 6 keys.
    132 
    133 Processing combos has two buffers, one for the key presses, another for the combos being activated. Use the following options to configure the sizes of these buffers:
    134 
    135 | Define                              | Default                                              |
    136 |-------------------------------------|------------------------------------------------------|
    137 | `#define COMBO_KEY_BUFFER_LENGTH 8` | 8 (the key amount `(EXTRA_)EXTRA_LONG_COMBOS` gives) |
    138 | `#define COMBO_BUFFER_LENGTH 4`     | 4                                                    |
    139 
    140 ### Modifier Combos
    141 If a combo resolves to a Modifier, the window for processing the combo can be extended independently from normal combos. By default, this is disabled but can be enabled with `#define COMBO_MUST_HOLD_MODS`, and the time window can be configured with `#define COMBO_HOLD_TERM 150` (default: `TAPPING_TERM`). With `COMBO_MUST_HOLD_MODS`, you cannot tap the combo any more which makes the combo less prone to misfires.
    142 
    143 ### Strict key press order
    144 By defining `COMBO_MUST_PRESS_IN_ORDER` combos only activate when the keys are pressed in the same order as they are defined in the key array.
    145 
    146 ### Per Combo Timing, Holding, Tapping and Key Press Order
    147 For each combo, it is possible to configure the time window it has to pressed in, if it needs to be held down, if it needs to be tapped, or if its keys need to be pressed in order.
    148 
    149 For example, tap-only combos are useful if any (or all) of the underlying keys are mod-tap or layer-tap keys. When you tap the combo, you get the combo result. When you press the combo and hold it down, the combo doesn't activate. Instead the keys are processed separately as if the combo wasn't even there.
    150 
    151 In order to use these features, the following configuration options and functions need to be defined. Coming up with useful timings and configuration is left as an exercise for the reader.
    152 
    153 | Config Flag                 | Function                                                  | Description                                                                                            |
    154 |-----------------------------|-----------------------------------------------------------|--------------------------------------------------------------------------------------------------------|
    155 | `COMBO_TERM_PER_COMBO`      | `uint16_t get_combo_term(uint16_t combo_index, combo_t *combo)`  | Optional per-combo timeout window. (default: `COMBO_TERM`)                                             |
    156 | `COMBO_MUST_HOLD_PER_COMBO` | `bool get_combo_must_hold(uint16_t combo_index, combo_t *combo)` | Controls if a given combo should fire immediately on tap or if it needs to be held. (default: `false`) |
    157 | `COMBO_MUST_TAP_PER_COMBO`  | `bool get_combo_must_tap(uint16_t combo_index, combo_t *combo)`  | Controls if a given combo should fire only if tapped within `COMBO_HOLD_TERM`. (default: `false`)      |
    158 | `COMBO_MUST_PRESS_IN_ORDER_PER_COMBO` | `bool get_combo_must_press_in_order(uint16_t combo_index, combo_t *combo)` | Controls if a given combo should fire only if its keys are pressed in order. (default: `true`) |
    159 
    160 Examples:
    161 ```c
    162 #ifdef COMBO_TERM_PER_COMBO
    163 uint16_t get_combo_term(uint16_t combo_index, combo_t *combo) {
    164     // decide by combo->keycode
    165     switch (combo->keycode) {
    166         case KC_X:
    167             return 50;
    168     }
    169 
    170     // or with combo index, i.e. its name from enum.
    171     switch (combo_index) {
    172         case COMBO_NAME_HERE:
    173             return 9001;
    174     }
    175 
    176     // And if you're feeling adventurous, you can even decide by the keys in the chord,
    177     // i.e. the exact array of keys you defined for the combo.
    178     // This can be useful if your combos have a common key and you want to apply the
    179     // same combo term for all of them.
    180     if (combo->keys[0] == KC_ENT) { // if first key in the array is Enter
    181         return 150;
    182     }
    183 
    184     return COMBO_TERM;
    185 }
    186 #endif
    187 
    188 #ifdef COMBO_MUST_HOLD_PER_COMBO
    189 bool get_combo_must_hold(uint16_t combo_index, combo_t *combo) {
    190     // Same as above, decide by keycode, the combo index, or by the keys in the chord.
    191 
    192     if (KEYCODE_IS_MOD(combo->keycode) || 
    193         (combo->keycode >= QK_MOMENTARY && combo->keycode <= QK_MOMENTARY_MAX) // MO(kc) keycodes
    194         ) {
    195         return true;
    196     }
    197 
    198     switch (combo_index) {
    199         case COMBO_NAME_HERE:
    200             return true;
    201     }
    202 
    203     return false;
    204 }
    205 #endif
    206 
    207 #ifdef COMBO_MUST_TAP_PER_COMBO
    208 bool get_combo_must_tap(uint16_t combo_index, combo_t *combo) {
    209     // If you want all combos to be tap-only, just uncomment the next line
    210     // return true
    211 
    212     // If you want *all* combos, that have Mod-Tap/Layer-Tap/Momentary keys in its chord, to be tap-only, this is for you:
    213     uint16_t key;
    214     uint8_t idx = 0;
    215     while ((key = pgm_read_word(&combo->keys[idx])) != COMBO_END) {
    216         switch (key) {
    217             case QK_MOD_TAP...QK_MOD_TAP_MAX:
    218             case QK_LAYER_TAP...QK_LAYER_TAP_MAX:
    219             case QK_MOMENTARY...QK_MOMENTARY_MAX:
    220                 return true;
    221         }
    222         idx += 1;
    223     }
    224     return false;
    225 
    226 }
    227 #endif
    228 
    229 #ifdef COMBO_MUST_PRESS_IN_ORDER_PER_COMBO
    230 bool get_combo_must_press_in_order(uint16_t combo_index, combo_t *combo) {
    231     switch (combo_index) {
    232         /* List combos here that you want to only activate if their keys
    233          * are pressed in the same order as they are defined in the combo's key
    234          * array. */
    235         case COMBO_NAME_HERE:
    236             return true;
    237         default:
    238             return false;
    239     }
    240 }
    241 #endif
    242 ```
    243 
    244 ### Generic hook to (dis)allow a combo activation
    245 
    246 By defining `COMBO_SHOULD_TRIGGER` and its companying function `bool combo_should_trigger(uint16_t combo_index, combo_t *combo, uint16_t keycode, keyrecord_t *record)` you can block or allow combos to activate on the conditions of your choice.
    247 For example, you could disallow some combos on the base layer and allow them on another. Or disable combos on the home row when a timer is running.
    248 
    249 Examples:
    250 ```c
    251 bool combo_should_trigger(uint16_t combo_index, combo_t *combo, uint16_t keycode, keyrecord_t *record) {
    252     /* Disable combo `SOME_COMBO` on layer `_LAYER_A` */
    253     switch (combo_index) {
    254         case SOME_COMBO:
    255             if (layer_state_is(_LAYER_A)) {
    256                 return false;
    257             }
    258     }
    259 
    260     return true;
    261 }
    262 ```
    263 
    264 ### Combo timer
    265 
    266 Normally, the timer is started on the first key press and then reset on every subsequent key press within the `COMBO_TERM`.
    267 Inputting combos is relaxed like this, but also slightly more prone to accidental misfires.
    268 
    269 The next two options alter the behaviour of the timer.
    270 
    271 #### `#define COMBO_STRICT_TIMER`
    272 
    273 With `COMBO_STRICT_TIMER`, the timer is started only on the first key press.
    274 Inputting combos is now less relaxed; you need to make sure the full chord is pressed within the `COMBO_TERM`.
    275 Misfires are less common but if you type multiple combos fast, there is a
    276 chance that the latter ones might not activate properly.
    277 
    278 #### `#define COMBO_NO_TIMER`
    279 
    280 By defining `COMBO_NO_TIMER`, the timer is disabled completely and combos are activated on the first key release.
    281 This also disables the "must hold" functionalities as they just wouldn't work at all.
    282 
    283 ### Customizable key releases
    284 
    285 By defining `COMBO_PROCESS_KEY_RELEASE` and implementing the function `bool process_combo_key_release(uint16_t combo_index, combo_t *combo, uint8_t key_index, uint16_t keycode)`, you can run your custom code on each key release after a combo was activated. For example you could change the RGB colors, activate haptics, or alter the modifiers.
    286 
    287 You can also release a combo early by returning `true` from the function.
    288 
    289 Here's an example where a combo resolves to two modifiers, and on key releases the modifiers are unregistered one by one, depending on which key was released.
    290 
    291 ```c
    292 enum combos {
    293   AB_MODS
    294 };
    295 
    296 const uint16_t PROGMEM ab_combo[] = {KC_A, KC_B, COMBO_END};
    297 
    298 combo_t key_combos[] = {
    299   [AB_MODS] = COMBO(ab_combo, LCTL(KC_LSFT)),
    300 };
    301 
    302 bool process_combo_key_release(uint16_t combo_index, combo_t *combo, uint8_t key_index, uint16_t keycode) {
    303     switch (combo_index) {
    304         case AB_MODS:
    305             switch(keycode) {
    306                 case KC_A:
    307                     unregister_mods(MOD_MASK_CTRL);
    308                     break;
    309                 case KC_B:
    310                     unregister_mods(MOD_MASK_SHIFT);
    311                     break;
    312             }
    313             return false; // do not release combo
    314     }
    315     return false;
    316 }
    317 ```
    318 
    319 ### Customizable key repress
    320 By defining `COMBO_PROCESS_KEY_REPRESS` and implementing `bool process_combo_key_repress(uint16_t combo_index, combo_t *combo, uint8_t key_index, uint16_t keycode)` you can run your custom code when you repress just released key of a combo. By combining it with custom `process_combo_event` we can for example make special handling for Alt+Tab to switch windows, which, on combo F+G activation, registers Alt and presses Tab - then we can switch windows forward by releasing G and pressing it again, or backwards with F key. Here's the full example:
    321 
    322 ```c
    323 enum combos {
    324     CMB_ALTTAB
    325 };
    326 
    327 const uint16_t PROGMEM combo_alttab[] = {KC_F, KC_G, COMBO_END};
    328 
    329 combo_t key_combos[COMBO_LENGTH] = {
    330     [CMB_ALTTAB] = COMBO(combo_alttab, KC_NO), // KC_NO to leave processing for process_combo_event
    331 };
    332 
    333 void process_combo_event(uint16_t combo_index, bool pressed) {
    334     switch (combo_index) {
    335         case CMB_ALTTAB:
    336             if (pressed) {
    337                 register_mods(MOD_LALT);
    338                 tap_code(KC_TAB);
    339             } else {
    340                 unregister_mods(MOD_LALT);
    341             }
    342             break;
    343     }
    344 }
    345 
    346 bool process_combo_key_repress(uint16_t combo_index, combo_t *combo, uint8_t key_index, uint16_t keycode) {
    347     switch (combo_index) {
    348         case CMB_ALTTAB:
    349             switch (keycode) {
    350                 case KC_F:
    351                     tap_code16(S(KC_TAB));
    352                     return true;
    353                 case KC_G:
    354                     tap_code(KC_TAB);
    355                     return true;
    356             }
    357     }
    358     return false;
    359 }
    360 ```
    361 
    362 ### Layer independent combos
    363 
    364 If you, for example, use multiple base layers for different key layouts, one for QWERTY, and another one for Colemak, you might want your combos to work from the same key positions on all layers. Defining the same combos again for another layout is redundant and takes more memory. The solution is to just check the keycodes from one layer.
    365 
    366 With `#define COMBO_ONLY_FROM_LAYER 0` in config.h, the combos' keys are always checked from layer `0`, even if other layers are active.
    367 
    368 #### Combo reference layers by layer.
    369 
    370 If not using `COMBO_ONLY_FROM_LAYER` it is possible to specify a
    371 combo reference layer for any layer using the `combo_ref_from_layer` hook. 
    372 The combo macros automatically create this function from the `COMBO_REF_LAYER()`
    373 entries given.
    374 
    375 This function returns the assigned reference layer for the current layer.
    376 if there is no match, it returns the  default reference layer if set, 
    377 or the current layer otherwise. A default layer can be set with
    378 `DEFAULT_REF_LAYER(_MY_COMBO_REF_LAYER)`
    379 
    380 If not set, the default reference layer selection from the automatically generated 
    381 `combo-ref-from-layer()` will be the current layer.
    382 
    383 The following `combo_ref_from_layer` function 
    384 will give a reference layer of _QWERTY for the _DVORAK layer and
    385 will give the _NAV layer as a reference to it's self. All other layers
    386 will have the default for their combo reference layer. If the default
    387 is not set, all other layers will reference themselves.
    388 
    389 ```c
    390 #define COMBO_REF_DEFAULT _MY_COMBO_LAYER
    391 
    392 uint8_t combo_ref_from_layer(uint8_t layer){
    393     switch (get_highest_layer(layer_state)){
    394         case _DVORAK: return _QWERTY;
    395         case _NAV: return _NAV;
    396         default: return _MY_COMBO_LAYER;
    397     }
    398     return layer;  // important if default is not in case.
    399 }
    400 ```
    401 
    402 The equivalent definition using the combo macros is this:
    403 
    404 ```c
    405 COMBO_REF_LAYER(_DVORAK, _QWERTY)
    406 COMBO_REF_LAYER(_NAV, _NAV)
    407 DEFAULT_REF_LAYER(_MY_COMBO_LAYER).
    408 ```
    409     
    410 
    411 ## User callbacks
    412 
    413 In addition to the keycodes, there are a few functions that you can use to set the status, or check it:
    414 
    415 |Function   |Description                                                         |
    416 |-----------|--------------------------------------------------------------------|
    417 | `combo_enable()`     | Enables the combo feature                               |
    418 | `combo_disable()`    | Disables the combo feature, and clears the combo buffer |
    419 | `combo_toggle()`     | Toggles the state of the combo feature                  |
    420 | `is_combo_enabled()` | Returns the status of the combo feature state (true or false) |
    421 
    422 
    423 ## Dictionary Management
    424 
    425 Having 3 places to update when adding new combos or altering old ones does become cumbersome when you have a lot of combos. We can alleviate this with some magic! ... If you consider C macros magic.
    426 First, you need to add `VPATH += keyboards/gboards` to your `rules.mk`. Next, include the file `g/keymap_combo.h` in your `keymap.c`.
    427 
    428 ::: warning
    429 This functionality uses the same `process_combo_event` function as `COMBO_ACTION` macros do, so you cannot use the function yourself in your keymap. Instead, you have to define the `case`s of the `switch` statement by themselves within `inject.h`, which `g/keymap_combo.h` will then include into the function.
    430 :::
    431 
    432 Then, write your combos in `combos.def` file in the following manner:
    433 
    434 ```c
    435 // Alternate reference layers by layer
    436 //               Layer     Reference layer
    437 COMBO_REF_LAYER(_DVORAK, _QWERTY)  // reference the qwerty layer for dvorak.
    438 COMBO_REF_LAYER(_NAV, _NAV) // explicit reference to self instead of the default.
    439 
    440 //   name     result    chord keys
    441 COMB(AB_ESC,   KC_ESC,   KC_A, KC_B)
    442 COMB(JK_TAB,   KC_TAB,   KC_J, KC_K)
    443 COMB(JKL_SPC,  KC_SPC,   KC_J, KC_K, KC_L)
    444 COMB(BSSL_CLR, KC_NO,    KC_BSPC, KC_LSFT) // using KC_NO as the resulting keycode is the same as COMBO_ACTION before.
    445 COMB(QW_UNDO,  C(KC_Z),  KC_Q, KC_W)
    446 SUBS(TH_THE,   "the",    KC_T, KC_H) // SUBS uses SEND_STRING to output the given string.
    447 ...
    448 ```
    449 
    450 For small to huge ready made dictionaries of combos, you can check out http://combos.gboards.ca/.