aboutsummaryrefslogtreecommitdiff
path: root/docs
diff options
context:
space:
mode:
authorPete Sevander <pete.sevander@gmail.com>2021-08-06 02:44:57 +0300
committerGitHub <noreply@github.com>2021-08-06 09:44:57 +1000
commit7e983796e18e7401c062c158f23966aeb7b1405b (patch)
tree88d6897d7ca4f6103f03c1941dac6736ba477361 /docs
parent07553b41f0a03ca6549c09cecf9cd3dec7332346 (diff)
downloadqmk_firmware-7e983796e18e7401c062c158f23966aeb7b1405b.tar.gz
qmk_firmware-7e983796e18e7401c062c158f23966aeb7b1405b.zip
Process combos earlier & overlapping combos (#8591)
* Combo processing improvements. Now it is possible to use ModTap and LayerTap keys as part of combos. Overlapping combos also don't trigger all the combos, just exactly the one that you press. New settings: - COMBO_MUST_HOLD_MODS - COMBO_MOD_TERM - COMBO_TERM_PER_COMBO - COMBO_MUST_HOLD_PER_COMBO - COMBO_STRICT_TIMER - COMBO_NO_TIMER * Remove the size flags from combo_t struct boolean members. This in the end actually saves space as the members are accessed so many times. The amount of operations needed to access the bits uses more memory than setting the size saves. * Fix `process_combo_key_release` not called correctly with tap-only combos * Fix not passing a pointer when NO_ACTION_TAPPING is defined. * Docs for `COMBO_ONLY_FROM_LAYER` * Update docs/feature_combo.md Co-authored-by: precondition <57645186+precondition@users.noreply.github.com> * Update quantum/process_keycode/process_combo.c Co-authored-by: precondition <57645186+precondition@users.noreply.github.com> * Add `EXTRA_SHORT_COMBOS` option. Stuff combo's `disabled` and `active` flags into `state`. Possibly can save some space. * Add more examples and clarify things with dict management system. - Simple examples now has a combo that has modifiers included. - The slightly more advanced examples now are actually more advanced instead of just `tap_code16(<modded-keycode>)`. - Added a note that `COMBO_ACTION`s are not needed anymore as you can just use custom keycodes. - Added a note that the `g/keymap_combo.h` macros use the `process_combo_event` function and that it is not usable in one's keymap afterwards. * Update docs/feature_combo.md Co-authored-by: precondition <57645186+precondition@users.noreply.github.com> * Update docs/feature_combo.md Co-authored-by: precondition <57645186+precondition@users.noreply.github.com> * Update docs/feature_combo.md Co-authored-by: precondition <57645186+precondition@users.noreply.github.com> * Update docs/feature_combo.md Co-authored-by: precondition <57645186+precondition@users.noreply.github.com> * Update docs/feature_combo.md Co-authored-by: precondition <57645186+precondition@users.noreply.github.com> * Change "the" combo action example to "email" example. * Update docs/feature_combo.md Co-authored-by: precondition <57645186+precondition@users.noreply.github.com> * Fix sneaky infinite loop with `combo_disable()` No need to call `dump_key_buffer` when disabling combos because the buffer is either being dumped if a combo-key was pressed, or the buffer is empty if a non-combo-key is pressed. * Update docs/feature_combo.md Co-authored-by: precondition <57645186+precondition@users.noreply.github.com> * Update docs/feature_combo.md Co-authored-by: precondition <57645186+precondition@users.noreply.github.com> Co-authored-by: precondition <57645186+precondition@users.noreply.github.com> Co-authored-by: Drashna Jaelre <drashna@live.com>
Diffstat (limited to 'docs')
-rw-r--r--docs/config_options.md14
-rw-r--r--docs/feature_combo.md292
2 files changed, 272 insertions, 34 deletions
diff --git a/docs/config_options.md b/docs/config_options.md
index 0c98b3101..78c1f70fd 100644
--- a/docs/config_options.md
+++ b/docs/config_options.md
@@ -188,9 +188,21 @@ If you define these options you will enable the associated feature, which may in
188 few ms of delay from this. But if you're doing chording on something with 3-4ms 188 few ms of delay from this. But if you're doing chording on something with 3-4ms
189 scan times? You probably want this. 189 scan times? You probably want this.
190* `#define COMBO_COUNT 2` 190* `#define COMBO_COUNT 2`
191 * Set this to the number of combos that you're using in the [Combo](feature_combo.md) feature. 191 * Set this to the number of combos that you're using in the [Combo](feature_combo.md) feature. Or leave it undefined and programmatically set the count.
192* `#define COMBO_TERM 200` 192* `#define COMBO_TERM 200`
193 * how long for the Combo keys to be detected. Defaults to `TAPPING_TERM` if not defined. 193 * how long for the Combo keys to be detected. Defaults to `TAPPING_TERM` if not defined.
194* `#define COMBO_MUST_HOLD_MODS`
195 * Flag for enabling extending timeout on Combos containing modifers
196* `#define COMBO_MOD_TERM 200`
197 * Allows for extending COMBO_TERM for mod keys while mid-combo.
198* `#define COMBO_MUST_HOLD_PER_COMBO`
199 * Flag to enable per-combo COMBO_TERM extension and `get_combo_must_hold()` function
200* `#define COMBO_TERM_PER_COMBO`
201 * Flag to enable per-combo COMBO_TERM extension and `get_combo_term()` function
202* `#define COMBO_STRICT_TIMER`
203 * Only start the combo timer on the first key press instead of on all key presses.
204* `#define COMBO_NO_TIMER`
205 * Disable the combo timer completely for relaxed combos.
194* `#define TAP_CODE_DELAY 100` 206* `#define TAP_CODE_DELAY 100`
195 * Sets the delay between `register_code` and `unregister_code`, if you're having issues with it registering properly (common on VUSB boards). The value is in milliseconds. 207 * Sets the delay between `register_code` and `unregister_code`, if you're having issues with it registering properly (common on VUSB boards). The value is in milliseconds.
196* `#define TAP_HOLD_CAPS_DELAY 80` 208* `#define TAP_HOLD_CAPS_DELAY 80`
diff --git a/docs/feature_combo.md b/docs/feature_combo.md
index d831328f6..d98e6f2ac 100644
--- a/docs/feature_combo.md
+++ b/docs/feature_combo.md
@@ -1,24 +1,39 @@
1# Combos 1# Combos
2 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 `S` within the tapping term would hit `ESC` instead, or have it perform even more complex tasks. 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 `S` within the combo term would hit `ESC` instead, or have it perform even more complex tasks.
4 4
5To enable this feature, you need to add `COMBO_ENABLE = yes` to your `rules.mk`. 5To enable this feature, you need to add `COMBO_ENABLE = yes` to your `rules.mk`.
6 6
7Additionally, in your `config.h`, you'll need to specify the number of combos that you'll be using, by adding `#define COMBO_COUNT 1` (replacing 1 with the number that you're using). 7Additionally, in your `config.h`, you'll need to specify the number of combos that you'll be using, by adding `#define COMBO_COUNT 1` (replacing 1 with the number that you're using). It is also possible to not define this and instead set the variable `COMBO_LEN` yourself. There's a trick where we don't need to think about this variable at all. More on this later.
8<!-- At this time, this is necessary -->
9 8
10Also, by default, the tapping term for the Combos is set to the same value as `TAPPING_TERM` (200 by default on most boards). But you can specify a different value by defining it in your `config.h`. For instance: `#define COMBO_TERM 300` would set the time out period for combos to 300ms.
11 9
12Then, 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 it's resulting action. 10Then, 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.
13 11
14```c 12```c
15const uint16_t PROGMEM test_combo[] = {KC_A, KC_B, COMBO_END}; 13const uint16_t PROGMEM test_combo1[] = {KC_A, KC_B, COMBO_END};
16combo_t key_combos[COMBO_COUNT] = {COMBO(test_combo, KC_ESC)}; 14const uint16_t PROGMEM test_combo2[] = {KC_C, KC_D, COMBO_END};
15combo_t key_combos[COMBO_COUNT] = {
16 COMBO(test_combo1, KC_ESC),
17 COMBO(test_combo2, LCTL(KC_Z)), // keycodes with modifiers are possible too!
18};
17``` 19```
18 20
19This will send "Escape" if you hit the A and B keys. 21This will send "Escape" if you hit the A and B keys, and Ctrl+Z when you hit the C and D keys.
22
23As of [PR#8591](https://github.com/qmk/qmk_firmware/pull/8591/), it is possible to fire combos from ModTap keys and LayerTap keys. So in the above example you could have keys `LSFT_T(KC_A)` and `LT(_LAYER, KC_B)` and it would work. So Home Row Mods and Home Row Combos at same time is now a thing!
20 24
21!> This method only supports [basic keycodes](keycodes_basic.md). See the examples for more control. 25It is also now 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.
26
27```c
28const uint16_t PROGMEM test_combo1[] = {LSFT_T(KC_A), LT(_LAYER, KC_B), COMBO_END};
29const uint16_t PROGMEM test_combo2[] = {LSFT_T(KC_A), LT(_LAYER, KC_B), KC_C, COMBO_END};
30combo_t key_combos[COMBO_COUNT] = {
31 COMBO(test_combo1, KC_ESC)
32 COMBO(test_combo2, KC_TAB)
33};
34```
35
36Executing more complex keycodes like ModTaps and LayerTaps is now also possible.
22 37
23## Examples 38## Examples
24 39
@@ -27,63 +42,68 @@ If you want to add a list, then you'd use something like this:
27```c 42```c
28enum combos { 43enum combos {
29 AB_ESC, 44 AB_ESC,
30 JK_TAB 45 JK_TAB,
46 QW_SFT,
47 SD_LAYER,
31}; 48};
32 49
33const uint16_t PROGMEM ab_combo[] = {KC_A, KC_B, COMBO_END}; 50const uint16_t PROGMEM ab_combo[] = {KC_A, KC_B, COMBO_END};
34const uint16_t PROGMEM jk_combo[] = {KC_J, KC_K, COMBO_END}; 51const uint16_t PROGMEM jk_combo[] = {KC_J, KC_K, COMBO_END};
52const uint16_t PROGMEM qw_combo[] = {KC_Q, KC_W, COMBO_END};
53const uint16_t PROGMEM sd_combo[] = {KC_S, KC_D, COMBO_END};
35 54
36combo_t key_combos[COMBO_COUNT] = { 55combo_t key_combos[COMBO_COUNT] = {
37 [AB_ESC] = COMBO(ab_combo, KC_ESC), 56 [AB_ESC] = COMBO(ab_combo, KC_ESC),
38 [JK_TAB] = COMBO(jk_combo, KC_TAB) 57 [JK_TAB] = COMBO(jk_combo, KC_TAB),
58 [QW_SFT] = COMBO(qw_combo, KC_LSFT)
59 [SD_LAYER] = COMBO(layer_combo, MO(_LAYER)),
39}; 60};
40``` 61```
41 62
42For a more complicated implementation, you can use the `process_combo_event` function to add custom handling. 63For a more complicated implementation, you can use the `process_combo_event` function to add custom handling.
64Additionally, this example shows how you can leave `COMBO_COUNT` undefined.
43 65
44```c 66```c
45enum combo_events { 67enum combo_events {
46 ZC_COPY, 68 EM_EMAIL,
47 XV_PASTE 69 BSPC_LSFT_CLEAR,
70 COMBO_LENGTH
48}; 71};
72uint16_t COMBO_LEN = COMBO_LENGTH; // remove the COMBO_COUNT define and use this instead!
49 73
50const uint16_t PROGMEM copy_combo[] = {KC_Z, KC_C, COMBO_END}; 74const uint16_t PROGMEM email_combo[] = {KC_E, KC_M, COMBO_END};
51const uint16_t PROGMEM paste_combo[] = {KC_X, KC_V, COMBO_END}; 75const uint16_t PROGMEM clear_line_combo[] = {KC_BSPC, KC_LSFT, COMBO_END};
52 76
53combo_t key_combos[COMBO_COUNT] = { 77combo_t key_combos[] = {
54 [ZC_COPY] = COMBO_ACTION(copy_combo), 78 [EM_EMAIL] = COMBO_ACTION(email_combo),
55 [XV_PASTE] = COMBO_ACTION(paste_combo), 79 [BSPC_LSFT_CLEAR] = COMBO_ACTION(clear_line_combo),
56}; 80};
81/* COMBO_ACTION(x) is same as COMBO(x, KC_NO) */
57 82
58void process_combo_event(uint16_t combo_index, bool pressed) { 83void process_combo_event(uint16_t combo_index, bool pressed) {
59 switch(combo_index) { 84 switch(combo_index) {
60 case ZC_COPY: 85 case EM_EMAIL:
61 if (pressed) { 86 if (pressed) {
62 tap_code16(LCTL(KC_C)); 87 SEND_STRING("john.doe@example.com");
63 } 88 }
64 break; 89 break;
65 case XV_PASTE: 90 case BSPC_LSFT_CLEAR:
66 if (pressed) { 91 if (pressed) {
67 tap_code16(LCTL(KC_V)); 92 tap_code16(KC_END);
93 tap_code16(S(KC_HOME));
94 tap_code16(KC_BSPC);
68 } 95 }
69 break; 96 break;
70 } 97 }
71} 98}
72``` 99```
73 100
74This will send Ctrl+C if you hit Z and C, and Ctrl+V if you hit X and V. But you could change this to do stuff like change layers, play sounds, or change settings. 101This 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.
75
76## Additional Configuration
77 102
78If you're using long combos, or even longer combos, you may run into issues with this, as the structure may not be large enough to accommodate what you're doing. 103It 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>)`.
79 104
80In this case, you can add either `#define EXTRA_LONG_COMBOS` or `#define EXTRA_EXTRA_LONG_COMBOS` in your `config.h` file. 105## Keycodes
81 106You 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`
82You may also be able to enable action keys by defining `COMBO_ALLOW_ACTION_KEYS`.
83
84## Keycodes
85
86You 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.
87 107
88|Keycode |Description | 108|Keycode |Description |
89|----------|---------------------------------| 109|----------|---------------------------------|
@@ -91,6 +111,187 @@ You can enable, disable and toggle the Combo feature on the fly. This is useful
91|`CMB_OFF` |Turns off Combo feature | 111|`CMB_OFF` |Turns off Combo feature |
92|`CMB_TOG` |Toggles Combo feature on and off | 112|`CMB_TOG` |Toggles Combo feature on and off |
93 113
114# Advanced Configuration
115These configuration settings can be set in your `config.h` file.
116
117## Combo Term
118By 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.
119
120## Buffer and state sizes
121If 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!
122
123To configure the amount of keys a combo can be composed of, change the following:
124
125| Keys | Define to be set |
126|------|-----------------------------------|
127| 6 | `#define EXTRA_SHORT_COMBOS` |
128| 8 | QMK Default |
129| 16 | `#define EXTRA_LONG_COMBOS` |
130| 32 | `#define EXTRA_EXTRA_LONG_COMBOS` |
131
132Defining `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.
133
134Processing 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:
135
136| Define | Default |
137|-------------------------------------|------------------------------------------------------|
138| `#define COMBO_KEY_BUFFER_LENGTH 8` | 8 (the key amount `(EXTRA_)EXTRA_LONG_COMBOS` gives) |
139| `#define COMBO_BUFFER_LENGTH 4` | 4 |
140
141## Modifier Combos
142If 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.
143
144## Per Combo Timing, Holding and Tapping
145For each combo, it is possible to configure the time window it has to pressed in, if it needs to be held down, or if it needs to be tapped.
146
147For example, tap-only combos are useful if any (or all) of the underlying keys is a Mod-Tap or a Layer-Tap key. When you tap the combo, you get the combo result. When you press the combo and hold it down, the combo doesn't actually activate. Instead the keys are processed separately as if the combo wasn't even there.
148
149In 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.
150
151| Config Flag | Function | Description |
152|-----------------------------|-----------------------------------------------------------|--------------------------------------------------------------------------------------------------------|
153| `COMBO_TERM_PER_COMBO` | uint16_t get_combo_term(uint16_t index, combo_t \*combo) | Optional per-combo timeout window. (default: `COMBO_TERM`) |
154| `COMBO_MUST_HOLD_PER_COMBO` | bool get_combo_must_hold(uint16_t index, combo_t \*combo) | Controls if a given combo should fire immediately on tap or if it needs to be held. (default: `false`) |
155| `COMBO_MUST_TAP_PER_COMBO` | bool get_combo_must_tap(uint16_t index, combo_t \*combo) | Controls if a given combo should fire only if tapped within `COMBO_HOLD_TERM`. (default: `false`) |
156
157Examples:
158```c
159uint16_t get_combo_term(uint16_t index, combo_t *combo) {
160 // decide by combo->keycode
161 switch (combo->keycode) {
162 case KC_X:
163 return 50;
164 }
165
166 // or with combo index, i.e. its name from enum.
167 switch (index) {
168 case COMBO_NAME_HERE:
169 return 9001;
170 }
171
172 // And if you're feeling adventurous, you can even decide by the keys in the chord,
173 // i.e. the exact array of keys you defined for the combo.
174 // This can be useful if your combos have a common key and you want to apply the
175 // same combo term for all of them.
176 if (combo->keys[0] == KC_ENTER) { // if first key in the array is KC_ENTER
177 return 150;
178 }
179
180 return COMBO_TERM;
181}
182
183bool get_combo_must_hold(uint16_t index, combo_t *combo) {
184 // Same as above, decide by keycode, the combo index, or by the keys in the chord.
185
186 if (KEYCODE_IS_MOD(combo->keycode) ||
187 (combo->keycode >= QK_MOMENTARY && combo->keycode <= QK_MOMENTARY_MAX) // MO(kc) keycodes
188 ) {
189 return true;
190 }
191
192 switch (index) {
193 case COMBO_NAME_HERE:
194 return true;
195 }
196
197 return false;
198}
199
200bool get_combo_must_tap(uint16_t index, combo_t *combo) {
201 // If you want all combos to be tap-only, just uncomment the next line
202 // return true
203
204 // If you want *all* combos, that have Mod-Tap/Layer-Tap/Momentary keys in its chord, to be tap-only, this is for you:
205 uint16_t key;
206 uint8_t idx = 0;
207 while ((key = pgm_read_word(&combo->keys[idx])) != COMBO_END) {
208 switch (key) {
209 case QK_MOD_TAP...QK_MOD_TAP_MAX:
210 case QK_LAYER_TAP...QK_LAYER_TAP_MAX:
211 case QK_MOMENTARY...QK_MOMENTARY_MAX:
212 return true;
213 }
214 idx += 1;
215 }
216 return false;
217
218}
219```
220
221## Variable Length Combos
222If you leave `COMBO_COUNT` undefined in `config.h`, it allows you to programmatically declare the size of the Combo data structure and avoid updating `COMBO_COUNT`. Instead a variable called `COMBO_LEN` has to be set. It can be set with something similar to the following in `keymap.c`: `uint16_t COMBO_LEN = sizeof(key_combos) / sizeof(key_combos[0]);` or by adding `COMBO_LENGTH` as the *last* entry in the combo enum and then `uint16_t COMBO_LEN = COMBO_LENGTH;` as such:
223```c
224enum myCombos {
225 ...,
226 COMBO_LENGTH
227};
228uint16_t COMBO_LEN = COMBO_LENGTH;
229```
230Regardless of the method used to declare `COMBO_LEN`, this also requires to convert the `combo_t key_combos[COMBO_COUNT] = {...};` line to `combo_t key_combos[] = {...};`.
231
232
233## Combo timer
234
235Normally, the timer is started on the first key press and then reset on every subsequent key press within the `COMBO_TERM`.
236Inputting combos is relaxed like this, but also slightly more prone to accidental misfires.
237
238The next two options alter the behaviour of the timer.
239
240### `#define COMBO_STRICT_TIMER`
241
242With `COMBO_STRICT_TIMER`, the timer is started only on the first key press.
243Inputting combos is now less relaxed; you need to make sure the full chord is pressed within the `COMBO_TERM`.
244Misfires are less common but if you type multiple combos fast, there is a
245chance that the latter ones might not activate properly.
246
247### `#define COMBO_NO_TIMER`
248
249By defining `COMBO_NO_TIMER`, the timer is disabled completely and combos are activated on the first key release.
250This also disables the "must hold" functionalities as they just wouldn't work at all.
251
252## Customizable key releases
253
254By 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.
255
256You can also release a combo early by returning `true` from the function.
257
258Here'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.
259
260```c
261enum combos {
262 AB_MODS,
263 COMBO_LENGTH
264};
265uint16_t COMBO_LEN = COMBO_LENGTH;
266
267const uint16_t PROGMEM ab_combo[] = {KC_A, KC_B, COMBO_END};
268
269combo_t key_combos[] = {
270 [AB_MODS] = COMBO(ab_combo, LCTL(KC_LSFT)),
271};
272
273bool process_combo_key_release(uint16_t combo_index, combo_t *combo, uint8_t key_index, uint16_t keycode) {
274 switch (combo_index) {
275 case AB_MODS:
276 switch(keycode) {
277 case KC_A:
278 unregister_mods(MOD_MASK_CTRL);
279 break;
280 case KC_B:
281 unregister_mods(MOD_MASK_SHIFT);
282 break;
283 }
284 return false; // do not release combo
285 }
286 return false;
287}
288```
289## Layer independent combos
290
291If 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.
292
293With `#define COMBO_ONLY_FROM_LAYER _LAYER_A` the combos' keys are always checked from layer `_LAYER_A` even though the active layer would be `_LAYER_B`.
294
94## User callbacks 295## User callbacks
95 296
96In addition to the keycodes, there are a few functions that you can use to set the status, or check it: 297In addition to the keycodes, there are a few functions that you can use to set the status, or check it:
@@ -101,3 +302,28 @@ In addition to the keycodes, there are a few functions that you can use to set t
101| `combo_disable()` | Disables the combo feature, and clears the combo buffer | 302| `combo_disable()` | Disables the combo feature, and clears the combo buffer |
102| `combo_toggle()` | Toggles the state of the combo feature | 303| `combo_toggle()` | Toggles the state of the combo feature |
103| `is_combo_enabled()` | Returns the status of the combo feature state (true or false) | 304| `is_combo_enabled()` | Returns the status of the combo feature state (true or false) |
305
306
307# Dictionary Management
308
309Having 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.
310First, you need to add `VPATH += keyboards/gboards` to your `rules.mk`. Next, include the file `g/keymap_combo.h` in your `keymap.c`.
311
312!> 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.
313
314Then, write your combos in `combos.def` file in the following manner:
315
316```c
317// name result chord keys
318COMB(AB_ESC, KC_ESC, KC_A, KC_B)
319COMB(JK_TAB, KC_TAB, KC_J, KC_K)
320COMB(JKL_SPC, KC_SPC, KC_J, KC_K, KC_L)
321COMB(BSSL_CLR, KC_NO, KC_BSPC, KC_LSFT) // using KC_NO as the resulting keycode is the same as COMBO_ACTION before.
322COMB(QW_UNDO, C(KC_Z), KC_Q, KC_W)
323SUBS(TH_THE, "the", KC_T, KC_H) // SUBS uses SEND_STRING to output the given string.
324...
325```
326
327Now, you can update only one place to add or alter combos. You don't even need to remember to update the `COMBO_COUNT` or the `COMBO_LEN` variables at all. Everything is taken care of. Magic!
328
329For small to huge ready made dictionaries of combos, you can check out http://combos.gboards.ca/.