Skip to content

Commit

Permalink
docs: Documented new combo approach
Browse files Browse the repository at this point in the history
  • Loading branch information
Nick-Munnich committed Jan 13, 2025
1 parent 40b4401 commit 610f357
Show file tree
Hide file tree
Showing 4 changed files with 138 additions and 21 deletions.
32 changes: 16 additions & 16 deletions docs/docs/config/combos.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,15 +11,16 @@ See [Configuration Overview](index.md) for instructions on how to change these s

Definition file: [zmk/app/Kconfig](https://github.com/zmkfirmware/zmk/blob/main/app/Kconfig)

| Config | Type | Description | Default |
| ------------------------------------- | ---- | -------------------------------------------------------------- | ------- |
| `CONFIG_ZMK_COMBO_MAX_PRESSED_COMBOS` | int | Maximum number of combos that can be active at the same time | 4 |
| `CONFIG_ZMK_COMBO_MAX_COMBOS_PER_KEY` | int | Maximum number of active combos that use the same key position | 5 |
| `CONFIG_ZMK_COMBO_MAX_KEYS_PER_COMBO` | int | Maximum number of keys to press to activate a combo | 4 |
| Config | Type | Description | Default |
| ----------------------------------------- | ---- | --------------------------------------------------------------------- | ------- |
| `ZMK_COMBO_MAX_TRIGGER_NUM` | int | Upper bound of numbers that can be passed to a combo trigger behavior | 20 |
| `CONFIG_ZMK_COMBO_MAX_PRESSED_COMBOS` | int | Maximum number of combos that can be active at the same time | 4 |
| `CONFIG_ZMK_COMBO_MAX_COMBOS_PER_TRIGGER` | int | Maximum number of active combos that use the same trigger | 5 |
| `CONFIG_ZMK_COMBO_MAX_TRIGGERS_PER_COMBO` | int | Maximum number of triggers required to activate a combo | 4 |

If `CONFIG_ZMK_COMBO_MAX_COMBOS_PER_KEY` is 5, you can have 5 separate combos that use position `0`, 5 combos that use position `1`, and so on.
If `CONFIG_ZMK_COMBO_MAX_COMBOS_PER_TRIGGER` is 5, you can have 5 separate combos that use trigger `0`, 5 combos that use trigger `1`, and so on.

If you want a combo that triggers when pressing 5 keys, you must set `CONFIG_ZMK_COMBO_MAX_KEYS_PER_COMBO` to 5.
If you want a combo that triggers after activating 5 different [combo triggers](../keymaps/behaviors/combo-trigger.md), you must set `CONFIG_ZMK_COMBO_MAX_KEYS_PER_COMBO` to 5.

## Devicetree

Expand All @@ -31,13 +32,12 @@ The `zmk,combos` node itself has no properties. It should have one child node pe

Each child node can have the following properties:

| Property | Type | Description | Default |
| ----------------------- | ------------- | ----------------------------------------------------------------------------------------------------------------------------------------- | ------------- |
| `bindings` | phandle-array | A [behavior](../keymaps/index.mdx#behaviors) to run when the combo is triggered | |
| `key-positions` | array | A list of key position indices for the keys which should trigger the combo | |
| `timeout-ms` | int | All the keys in `key-positions` must be pressed within this time in milliseconds to trigger the combo | 50 |
| `require-prior-idle-ms` | int | If any non-modifier key is pressed within `require-prior-idle-ms` before a key in the combo, the key will not be considered for the combo | -1 (disabled) |
| `slow-release` | bool | Releases the combo when all keys are released instead of when any key is released | false |
| `layers` | array | A list of layers on which the combo may be triggered. `-1` allows all layers. | `<-1>` |
| Property | Type | Description | Default |
| ----------------------- | ------------- | ------------------------------------------------------------------------------------------------------------------------------------------------- | ------------- |
| `bindings` | phandle-array | A [behavior](../keymaps/index.mdx#behaviors) to run when the combo is triggered | |
| `triggers` | array | A list of trigger ids which should trigger the combo | |
| `timeout-ms` | int | All the triggers in `triggers` must be activated within this time in milliseconds to trigger the combo | 50 |
| `require-prior-idle-ms` | int | If any non-modifier key is pressed within `require-prior-idle-ms` before a trigger in the combo, the trigger will not be considered for the combo | -1 (disabled) |
| `slow-release` | bool | Releases the combo when all keys are released instead of when any key is released | false |

The `key-positions` array must not be longer than the `CONFIG_ZMK_COMBO_MAX_KEYS_PER_COMBO` setting, which defaults to 4. If you want a combo that triggers when pressing 5 keys, then you must change the setting to 5.
The `triggers` array must not be longer than the `CONFIG_ZMK_COMBO_MAX_TRIGGERS_PER_COMBO` setting, which defaults to 4. If you want a combo that triggers when activating 5 triggers, then you must change the setting to 5.
66 changes: 66 additions & 0 deletions docs/docs/keymaps/behaviors/array.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
---
title: Array of Behaviors
sidebar_label: Array
---

## Summary

An array of behaviors is meant not to be placed in your keymap directly, but rather to simplify the usage of other behaviors such as [hold-tap](hold-tap.mdx), [combo-trigger](combo-trigger.md), or [mod-morph](mod-morph.md).

Invoking an array of behaviors with a particular integer will trigger the behavior at that location in the array (indexed from 0).

## Mod-Morph

### Configuration

Below is an example of how to implement an array with three elements.

```dts
/ {
arr: behavior_array {
compatible = "zmk,behavior-array";
#binding-cells = <1>;
bindings = <&mt LEFT_CONTROL A &kp B &lt 1 C>;
};
};
```

The above behavior array could be triggered like so:

```dts
&arr 0
```

The above would act like `&mt LEFT_CONTROL A`.

```dts
&arr 1
```

The above would act like `&kp B`.

```dts
&arr 2
```

The above would act like `&lt 1 C`.

The `&arr X` call could happen in your keymap, but it is more useful if it is used e.g. as the `fallback-behavior` parameter of a [combo-trigger](combo-trigger.md):

```dts
/ {
behaviors {
combo_mt: combo_trigger_or_key_press {
compatible = "zmk,behavior-combo-trigger";
#binding-cells = <2>;
display-name = "Combo or Mod Tap";
fallback-behavior = <&mt_arr>;
};
mt_arr: mod_tap_behavior_array {
compatible = "zmk,behavior-array";
#binding-cells = <1>;
bindings = <&mt LEFT_CONTROL A &mt LEFT_CONTROL B &mt LEFT_CONTROL C>;
};
};
};
```
52 changes: 52 additions & 0 deletions docs/docs/keymaps/behaviors/combo-trigger.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
---
title: Combo Trigger Behavior
sidebar_label: Combo Trigger
---

## Summary

The combo trigger behavior is a special type of behavior that is used together with the [`compatible = "zmk,combos";`](../combos.md) node to add combos to your keymap.

## Combo Trigger

### Configuration

Below is an example of how to implement the combo trigger "Combo or Key Press". When assigned to a key, it will send the combo trigger id of the first parameter passed to it on to the `combos` node. If no combo is triggered, the fallback behavior is triggered using the second parameter passed to the combo trigger.

```dts
/ {
behaviors {
combo_kp: combo_trigger_or_key_press {
compatible = "zmk,behavior-combo-trigger";
#binding-cells = <2>;
display-name = "Combo or Key Press";
fallback-behavior = <&kp>;
};
};
};
```

Note that this specific combo trigger behavior exists in ZMK by default using the binding `&combo_kp`.

### Behavior Binding

- Reference: `&combo_kp`
- Parameter: None

Example:

```dts
&combo_kp 1 A
```

When activated, the `combos` node receives the trigger `1`. If no combo is triggered, the activation acts like a `&kp A` instead.

### Fallback Behavior

It is assumed that the behavior in `fallback-behavior` accepts a single parameter as an argument. Hence the behavior should always be given without any arguments. If the behavior accepts no arguments, two parameters should still be passed to the combo trigger:

```dts
&combo_no_param_fallback 1 0
```

If the behavior you wish to have as a fallback-behavior accepts two parameters as arguments, it is recommended that you make use of the [array behavior](array.md).
9 changes: 4 additions & 5 deletions docs/docs/keymaps/combos.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,15 +8,15 @@ Combo keys are a way to combine multiple keypresses to output a different key. F

### Configuration

Combos configured in your `.keymap` file, but are separate from the `keymap` node found there, since they are processed before the normal keymap. They are specified like this:
Combos configured in your `.keymap` file, but are separate from the `keymap` node found there. Alongside other options, they define a list of `triggers` that need to be activated by [combo-triggers](behaviors/combo-trigger.md) within `timeout-ms` seconds of each other to activate the output found in `bindings`. They are specified like this:

```dts
/ {
combos {
compatible = "zmk,combos";
combo_esc {
timeout-ms = <50>;
key-positions = <0 1>;
triggers = <0 1>;
bindings = <&kp ESC>;
};
};
Expand All @@ -25,16 +25,15 @@ Combos configured in your `.keymap` file, but are separate from the `keymap` nod

- The name of the combo doesn't really matter, but convention is to start the node name with `combo_`.
- The `compatible` property should always be `"zmk,combos"` for combos.
- All the keys in `key-positions` must be pressed within `timeout-ms` milliseconds to trigger the combo.
- All the [combo-triggers](behaviors/combo-trigger.md) in `triggers` must be pressed within `timeout-ms` milliseconds to trigger the combo.
- `key-positions` is an array of key positions. See the info section below about how to figure out the positions on your board.
- `layers = <0 1...>` will allow limiting a combo to specific layers. This is an _optional_ parameter, when omitted it defaults to global scope.
- `bindings` is the behavior that is activated when the behavior is pressed.
- (advanced) you can specify `slow-release` if you want the combo binding to be released when all key-positions are released. The default is to release the combo as soon as any of the keys in the combo is released.
- (advanced) you can specify a `require-prior-idle-ms` value much like for [hold-taps](behaviors/hold-tap.mdx#require-prior-idle-ms). If any non-modifier key is pressed within `require-prior-idle-ms` before a key in the combo, the combo will not trigger.

:::info

Key positions are numbered like the keys in your keymap, starting at 0. So, if the first key in your keymap is `Q`, this key is in position `0`. The next key (possibly `W`) will have position 1, etcetera.
By default, triggers can be any non-negative number below 20. To increase (or decrease for memory savings) this limit, you will need to adjust the corresponding [`Kconfig flag`](../config/combos.md).

:::

Expand Down

0 comments on commit 610f357

Please sign in to comment.