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/.