keyboard stuff
at master 450 lines 20 kB view raw view rendered
1# Combos 2 3The 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 5To enable this feature, you need to add `COMBO_ENABLE = yes` to your `rules.mk`. 6 7Then, 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 10const uint16_t PROGMEM test_combo1[] = {KC_A, KC_B, COMBO_END}; 11const uint16_t PROGMEM test_combo2[] = {KC_C, KC_D, COMBO_END}; 12combo_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 18This 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 21Advanced 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 24const uint16_t PROGMEM test_combo1[] = {LSFT_T(KC_A), LT(1, KC_B), COMBO_END}; 25const uint16_t PROGMEM test_combo2[] = {TD(TD_ESC_CAPS), KC_F1, COMBO_END}; 26``` 27 28## Overlapping Combos 29It 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 32const uint16_t PROGMEM test_combo1[] = {LSFT_T(KC_A), LT(1, KC_B), COMBO_END}; 33const uint16_t PROGMEM test_combo2[] = {LSFT_T(KC_A), LT(1, KC_B), KC_C, COMBO_END}; 34combo_t key_combos[] = { 35 COMBO(test_combo1, KC_ESC) 36 COMBO(test_combo2, KC_TAB) 37}; 38``` 39 40## Examples 41 42A long list of combos can be defined in an `enum` list: 43 44```c 45enum combos { 46 AB_ESC, 47 JK_TAB, 48 QW_SFT, 49 SD_LAYER 50}; 51 52const uint16_t PROGMEM ab_combo[] = {KC_A, KC_B, COMBO_END}; 53const uint16_t PROGMEM jk_combo[] = {KC_J, KC_K, COMBO_END}; 54const uint16_t PROGMEM qw_combo[] = {KC_Q, KC_W, COMBO_END}; 55const uint16_t PROGMEM sd_combo[] = {KC_S, KC_D, COMBO_END}; 56 57combo_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 65For a more complicated implementation, you can use the `process_combo_event` function to add custom handling. 66 67```c 68enum combo_events { 69 EM_EMAIL, 70 BSPC_LSFT_CLEAR, 71}; 72 73const uint16_t PROGMEM email_combo[] = {KC_E, KC_M, COMBO_END}; 74const uint16_t PROGMEM clear_line_combo[] = {KC_BSPC, KC_LSFT, COMBO_END}; 75 76combo_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 82void 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 100This 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 102It 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 105You 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 114These configuration settings can be set in your `config.h` file. 115 116### Combo Term 117By 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 120If 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 122To 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 131Defining `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 133Processing 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 141If 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 144By 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 147For 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 149For 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 151In 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 160Examples: 161```c 162#ifdef COMBO_TERM_PER_COMBO 163uint16_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 189bool 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 208bool 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 230bool 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 246By 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. 247For 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 249Examples: 250```c 251bool 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 266Normally, the timer is started on the first key press and then reset on every subsequent key press within the `COMBO_TERM`. 267Inputting combos is relaxed like this, but also slightly more prone to accidental misfires. 268 269The next two options alter the behaviour of the timer. 270 271#### `#define COMBO_STRICT_TIMER` 272 273With `COMBO_STRICT_TIMER`, the timer is started only on the first key press. 274Inputting combos is now less relaxed; you need to make sure the full chord is pressed within the `COMBO_TERM`. 275Misfires are less common but if you type multiple combos fast, there is a 276chance that the latter ones might not activate properly. 277 278#### `#define COMBO_NO_TIMER` 279 280By defining `COMBO_NO_TIMER`, the timer is disabled completely and combos are activated on the first key release. 281This also disables the "must hold" functionalities as they just wouldn't work at all. 282 283### Customizable key releases 284 285By 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 287You can also release a combo early by returning `true` from the function. 288 289Here'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 292enum combos { 293 AB_MODS 294}; 295 296const uint16_t PROGMEM ab_combo[] = {KC_A, KC_B, COMBO_END}; 297 298combo_t key_combos[] = { 299 [AB_MODS] = COMBO(ab_combo, LCTL(KC_LSFT)), 300}; 301 302bool 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 320By 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 323enum combos { 324 CMB_ALTTAB 325}; 326 327const uint16_t PROGMEM combo_alttab[] = {KC_F, KC_G, COMBO_END}; 328 329combo_t key_combos[COMBO_LENGTH] = { 330 [CMB_ALTTAB] = COMBO(combo_alttab, KC_NO), // KC_NO to leave processing for process_combo_event 331}; 332 333void 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 346bool 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 364If 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 366With `#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 370If not using `COMBO_ONLY_FROM_LAYER` it is possible to specify a 371combo reference layer for any layer using the `combo_ref_from_layer` hook. 372The combo macros automatically create this function from the `COMBO_REF_LAYER()` 373entries given. 374 375This function returns the assigned reference layer for the current layer. 376if there is no match, it returns the default reference layer if set, 377or the current layer otherwise. A default layer can be set with 378`DEFAULT_REF_LAYER(_MY_COMBO_REF_LAYER)` 379 380If not set, the default reference layer selection from the automatically generated 381`combo-ref-from-layer()` will be the current layer. 382 383The following `combo_ref_from_layer` function 384will give a reference layer of _QWERTY for the _DVORAK layer and 385will give the _NAV layer as a reference to it's self. All other layers 386will have the default for their combo reference layer. If the default 387is not set, all other layers will reference themselves. 388 389```c 390#define COMBO_REF_DEFAULT _MY_COMBO_LAYER 391 392uint8_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 402The equivalent definition using the combo macros is this: 403 404```c 405COMBO_REF_LAYER(_DVORAK, _QWERTY) 406COMBO_REF_LAYER(_NAV, _NAV) 407DEFAULT_REF_LAYER(_MY_COMBO_LAYER). 408``` 409 410 411## User callbacks 412 413In 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 425Having 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. 426First, 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 429This 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 432Then, write your combos in `combos.def` file in the following manner: 433 434```c 435// Alternate reference layers by layer 436// Layer Reference layer 437COMBO_REF_LAYER(_DVORAK, _QWERTY) // reference the qwerty layer for dvorak. 438COMBO_REF_LAYER(_NAV, _NAV) // explicit reference to self instead of the default. 439 440// name result chord keys 441COMB(AB_ESC, KC_ESC, KC_A, KC_B) 442COMB(JK_TAB, KC_TAB, KC_J, KC_K) 443COMB(JKL_SPC, KC_SPC, KC_J, KC_K, KC_L) 444COMB(BSSL_CLR, KC_NO, KC_BSPC, KC_LSFT) // using KC_NO as the resulting keycode is the same as COMBO_ACTION before. 445COMB(QW_UNDO, C(KC_Z), KC_Q, KC_W) 446SUBS(TH_THE, "the", KC_T, KC_H) // SUBS uses SEND_STRING to output the given string. 447... 448``` 449 450For small to huge ready made dictionaries of combos, you can check out http://combos.gboards.ca/.