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