Skip to content

Commit

Permalink
docs: Documented parameterised tap dance and mod morph
Browse files Browse the repository at this point in the history
  • Loading branch information
Nick-Munnich committed Jan 3, 2025
1 parent ae249be commit 2825669
Show file tree
Hide file tree
Showing 3 changed files with 199 additions and 56 deletions.
52 changes: 35 additions & 17 deletions docs/docs/config/behaviors.md
Original file line number Diff line number Diff line change
Expand Up @@ -233,23 +233,28 @@ See the [mod-morph behavior](../keymaps/behaviors/mod-morph.md) documentation fo

### Devicetree

Definition file: [zmk/app/dts/bindings/behaviors/zmk,behavior-mod-morph.yaml](https://github.com/zmkfirmware/zmk/blob/main/app/dts/bindings/behaviors/zmk%2Cbehavior-mod-morph.yaml)
Definition files:

Applies to: `compatible = "zmk,behavior-mod-morph"`
- [zmk/app/dts/bindings/behaviors/zmk,behavior-mod-morph-param.yaml](https://github.com/zmkfirmware/zmk/blob/main/app/dts/bindings/behaviors/zmk%2Cbehavior-mod-morph.yaml)
- [zmk/app/dts/bindings/behaviors/zmk,behavior-mod-morph.yaml](https://github.com/zmkfirmware/zmk/blob/main/app/dts/bindings/behaviors/zmk%2Cbehavior-mod-morph.yaml)

| Property | Type | Description |
| ---------------- | ------------- | --------------------------------------------------------------------------------- |
| `#binding-cells` | int | Must be `<0>` |
| `bindings` | phandle array | A list of two behaviors: one for normal press and one for mod morphed press |
| `mods` | int | A bit field of modifiers. The morph behavior is used if any of these are pressed. |
| Property | Type | Description |
| ---------------- | ------------- | ----------------------------------------------------------------------------------------------------------------------------------------- |
| `compatible` | string | Mod-Morph variant, **must be _one_ of**:<ul><li>`"zmk,behavior-mod-morph-param"`</li><li>`"zmk,behavior-mod-morph"`</li></ul> |
| `#binding-cells` | int | Must be <ul><li>`<2>` if `compatible = "zmk,behavior-mod-morph-param"`</li><li>`<0>` if `compatible = "zmk,behavior-mod-morph"`</li></ul> |
| `bindings` | phandle array | A list of two behaviors: one for normal press and one for mod morphed press |
| `binding-params` | array | A list of two param assignment maps. Only applies to `compatible = "zmk,behavior-mod-morph-param"` |
| `mods` | int | A bit field of modifiers. The morph behavior is used if any of these are pressed. |

See [dt-bindings/zmk/modifiers.h](https://github.com/zmkfirmware/zmk/blob/main/app/include/dt-bindings/zmk/modifiers.h) for a list of modifiers.
The `binding-params` array should be constructed using the `BINDING_PARAM` macro.
See [here](../keymaps/behaviors/mod-morph.md#binding-parameters) for more details.

You can use the following nodes to tweak the default behaviors:

| Node | Behavior |
| -------- | ------------------------------------------------- |
| `&gresc` | [Grave escape](../keymaps/behaviors/mod-morph.md) |
| Node | Behavior |
| ----- | ------------------------------------------------------- |
| `&mm` | [Mod Morph Keypress](../keymaps/behaviors/mod-morph.md) |

## Sensor Rotation

Expand Down Expand Up @@ -327,15 +332,28 @@ See the [tap dance behavior](../keymaps/behaviors/tap-dance.mdx) documentation f

### Devicetree

Definition file: [zmk/app/dts/bindings/behaviors/zmk,behavior-tap-dance.yaml](https://github.com/zmkfirmware/zmk/blob/main/app/dts/bindings/behaviors/zmk%2Cbehavior-tap-dance.yaml)
Definition files:

- [zmk/app/dts/bindings/behaviors/zmk,behavior-tap-dance-param.yaml](https://github.com/zmkfirmware/zmk/blob/main/app/dts/bindings/behaviors/zmk%2Cbehavior-tap-dance.yaml)
- [zmk/app/dts/bindings/behaviors/zmk,behavior-tap-dance.yaml](https://github.com/zmkfirmware/zmk/blob/main/app/dts/bindings/behaviors/zmk%2Cbehavior-tap-dance.yaml)

| Property | Type | Description | Default |
| ----------------- | ------------- | ----------------------------------------------------------------------------------------------------------------------------------------- | ------- |
| `compatible` | string | Tap-Dance variant, **must be _one_ of**:<ul><li>`"zmk,behavior-tap-dance-param"`</li><li>`"zmk,behavior-tap-dance"`</li></ul> | |
| `#binding-cells` | int | Must be <ul><li>`<2>` if `compatible = "zmk,behavior-tap-dance-param"`</li><li>`<0>` if `compatible = "zmk,behavior-tap-dance"`</li></ul> | |
| `bindings` | phandle array | A list of behaviors from which to select | |
| `binding-params` | array | A list of two param assignment maps. Only applies to `compatible = "zmk,behavior-tap-dance-param"` | |
| `tapping-term-ms` | int | The maximum time (in milliseconds) between taps before an item from `bindings` is triggered. | 200 |

Applies to: `compatible = "zmk,behavior-tap-dance"`
See [dt-bindings/zmk/modifiers.h](https://github.com/zmkfirmware/zmk/blob/main/app/include/dt-bindings/zmk/modifiers.h) for a list of modifiers.
The `binding-params` array should be constructed using the `BINDING_PARAM` macro.
See [here](../keymaps/behaviors/tap-dance.mdx#binding-params) for more details.

You can use the following nodes to tweak the default behaviors:

| Property | Type | Description | Default |
| ----------------- | ------------- | -------------------------------------------------------------------------------------------- | ------- |
| `#binding-cells` | int | Must be `<0>` | |
| `bindings` | phandle array | A list of behaviors from which to select | |
| `tapping-term-ms` | int | The maximum time (in milliseconds) between taps before an item from `bindings` is triggered. | 200 |
| Node | Behavior |
| ----- | -------------------------------------------------------- |
| `&td` | [Tap Dance Keypress](../keymaps/behaviors/tap-dance.mdx) |

## Two Axis Input

Expand Down
108 changes: 82 additions & 26 deletions docs/docs/keymaps/behaviors/mod-morph.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,35 +12,41 @@ The mod-morph behavior invokes a different behavior depending on whether any of

## Mod-Morph

### Configuration
ZMK provides a simple build-in mod-morph behavior.
When pressed with one of `LEFT_SHIFT`, `LEFT_GUI`, `RIGHT_SHIFT`, `RIGHT_GUI` active, a [key press](key-press.md) will be triggered using the second parameter.
Otherwise, a key press with the first parameter will be triggered.

Below is an example of how to implement the mod-morph "Grave Escape". When assigned to a key, pressing the key on its own will send an
Escape keycode but pressing it while a shift or GUI modifier is held sends the grave `` ` `` keycode instead:
### Behavior Binding

- Reference: `&mm`
- Parameters: The keycode usage IDs from the usage page, e.g. `N4` or `A`

Example:

```dts
/ {
behaviors {
gresc: grave_escape {
compatible = "zmk,behavior-mod-morph";
#binding-cells = <0>;
bindings = <&kp ESC>, <&kp GRAVE>;
mods = <(MOD_LGUI|MOD_LSFT|MOD_RGUI|MOD_RSFT)>;
};
};
};
&mm A B
```

Note that this specific mod-morph exists in ZMK by default using the binding `&gresc`.
## Custom Mod-Morph

### Behavior Binding
If you want to trigger other behaviors or morph based on other combinations of modifiers, you can create a new mod-morph behavior.

- Reference: `&gresc`
- Parameter: None

Example:
Below is an example of how to implement a mod-morph that selects which behavior to trigger based on whether a `CTRL` modifier is active.
When `CTRL` is not active, a `&kp` with the first parameter passed to the behavior is triggered.
Otherwise, the layer whose number corresponds to the second parameter passed to the behavior is triggered.

```dts
&gresc
/ {
behaviors {
mm_ctrl: mod_morph_control {
compatible = "zmk,behavior-mod-morph-param";
#binding-cells = <2>;
mods = <(MOD_LCTL|MOD_RCTL)>;
bindings = <&kp PLACEHOLDER>, <&mo PLACEHOLDER>;
binding-params = <BINDING_PARAM(1,0) BINDING_PARAM(2,0)>;
};
};
};
```

### Mods
Expand All @@ -64,6 +70,37 @@ Example:
mods = <(MOD_LGUI|MOD_LSFT|MOD_RGUI|MOD_RSFT)>;
```

### Binding Parameters

The `binding-params` property determines how the parameters passed to the mod-morph behavior are passed on to the behaviors listed in `bindings`.
It is an array which always has two `BINDING_PARAM(arg1,arg2)` elements - the first corresponds to the first behavior listed in `bindings`, the second corresponding to the second. The number chosen for `argX` determines what the Xth parameter passed to the behavior will be:

- `1`: The Xth parameter passed will be the first parameter that the mod-morph behavior received
- `2`: The Xth parameter passed will be the second parameter that the mod-morph behavior received
- `0`: The Xth parameter will be that which is written in the `bindings` array.

Note that any parameters in `bindings` behaviors which are to be replaced should be set to `PLACEHOLDER`.

Examples:

```dts
binding-params = <BINDING_PARAM(1,2) BINDING_PARAM(0,0)>;
```

The first behavior receives both input parameters, the second receives no additional input parameters (receives those specified in the `bindings` array, if applicable).

```dts
binding-params = <BINDING_PARAM(2,0) BINDING_PARAM(1,0)>;
```

The first behavior receives the second input parameter as its first parameter (the second coming from the `bindings` array, if applicable), the second behavior receives the first input parameter as its first parameter (the second coming from the `bindings` array, if applicable).

```dts
binding-params = <BINDING_PARAM(0,0) BINDING_PARAM(0,0)>;
```

Neither behavior receives any input parameter that was not specified in the `bindings` array. Note that mod-morph always requires two parameters to be passed, so you will still need to write e.g. `&my_mm 0 0` in your keymap - the parameters passed will be ignored[^1]. This also applies if your `binding-params` only makes use of a single parameter, in which case you'd write e.g. `&my_mm A 0`.

### Advanced Configuration

#### `keep-mods`
Expand All @@ -76,9 +113,10 @@ For example, the following configuration morphs `LEFT_SHIFT` + `BACKSPACE` into
/ {
behaviors {
bspc_del: backspace_delete {
compatible = "zmk,behavior-mod-morph";
#binding-cells = <0>;
compatible = "zmk,behavior-mod-morph-param";
#binding-cells = <2>;
bindings = <&kp BACKSPACE>, <&kp DELETE>;
binding-params = <BINDING_PARAM(0,0) BINDING_PARAM(0,0)>;
mods = <(MOD_LSFT|MOD_RSFT)>;
keep-mods = <(MOD_RSFT)>;
};
Expand All @@ -97,15 +135,17 @@ As an example, consider the following two mod-morphs:
/ {
behaviors {
morph_BC: morph_BC {
compatible = "zmk,behavior-mod-morph";
#binding-cells = <0>;
compatible = "zmk,behavior-mod-morph-param";
#binding-cells = <2>;
bindings = <&kp B>, <&kp C>;
binding-params = <BINDING_PARAM(0,0) BINDING_PARAM(0,0)>;
mods = <(MOD_LCTL|MOD_RCTL)>;
};
morph_ABC: morph_ABC {
compatible = "zmk,behavior-mod-morph";
#binding-cells = <0>;
compatible = "zmk,behavior-mod-morph-param";
#binding-cells = <2>;
bindings = <&kp A>, <&morph_BC>;
binding-params = <BINDING_PARAM(0,0) BINDING_PARAM(0,0)>;
mods = <(MOD_LSFT|MOD_RSFT)>;
};
};
Expand All @@ -119,3 +159,19 @@ When you assign `&morph_ABC` to a key position and press it, it will output `A`
If the first modified key press sends the modifier along with the morphed keycode and [Karabiner-Elements](https://karabiner-elements.pqrs.org/) is running, disable the "Modify Events" toggle from Karabiner's "Devices" settings page for the keyboard running ZMK.

:::

[^1]: There exists a simplified version of mod-morph without any input parameters, `compatible="zmk,behavior-mod-morph"`:

```dts
/ {
behaviors {
bspc_del: backspace_delete {
compatible = "zmk,behavior-mod-morph";
#binding-cells = <0>;
bindings = <&kp BACKSPACE>, <&kp DELETE>;
mods = <(MOD_LSFT|MOD_RSFT)>;
keep-mods = <(MOD_RSFT)>;
};
};
};
```
Loading

0 comments on commit 2825669

Please sign in to comment.