diff --git a/docs/docs/config/behaviors.md b/docs/docs/config/behaviors.md
index 7df6d176608..49864ad9d70 100644
--- a/docs/docs/config/behaviors.md
+++ b/docs/docs/config/behaviors.md
@@ -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**:
- `"zmk,behavior-mod-morph-param"`
- `"zmk,behavior-mod-morph"`
|
+| `#binding-cells` | int | Must be - `<2>` if `compatible = "zmk,behavior-mod-morph-param"`
- `<0>` if `compatible = "zmk,behavior-mod-morph"`
|
+| `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
@@ -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**:- `"zmk,behavior-tap-dance-param"`
- `"zmk,behavior-tap-dance"`
| |
+| `#binding-cells` | int | Must be - `<2>` if `compatible = "zmk,behavior-tap-dance-param"`
- `<0>` if `compatible = "zmk,behavior-tap-dance"`
| |
+| `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
diff --git a/docs/docs/keymaps/behaviors/mod-morph.md b/docs/docs/keymaps/behaviors/mod-morph.md
index 879db998484..17fb51104b8 100644
--- a/docs/docs/keymaps/behaviors/mod-morph.md
+++ b/docs/docs/keymaps/behaviors/mod-morph.md
@@ -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 = ;
+ };
+ };
+};
```
### Mods
@@ -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 = ;
+```
+
+The first behavior receives both input parameters, the second receives neither.
+
+```dts
+binding-params = ;
+```
+
+The first behavior receives the second input parameter as its first parameter, the second behavior receives the first input parameter as its first parameter.
+
+```dts
+binding-params = ;
+```
+
+Neither behavior receives any input parameter. 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].
+
### Advanced Configuration
#### `keep-mods`
@@ -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 = ;
mods = <(MOD_LSFT|MOD_RSFT)>;
keep-mods = <(MOD_RSFT)>;
};
@@ -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 = ;
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 = ;
mods = <(MOD_LSFT|MOD_RSFT)>;
};
};
@@ -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)>;
+ };
+ };
+};
+```
diff --git a/docs/docs/keymaps/behaviors/tap-dance.mdx b/docs/docs/keymaps/behaviors/tap-dance.mdx
index 166d7813226..19abe1591af 100644
--- a/docs/docs/keymaps/behaviors/tap-dance.mdx
+++ b/docs/docs/keymaps/behaviors/tap-dance.mdx
@@ -8,23 +8,75 @@ import TabItem from "@theme/TabItem";
## Summary
-A tap-dance key invokes a different behavior (e.g. `kp`) corresponding to how many times it is pressed. For example, you could configure a tap-dance key that acts as `LSHIFT` if tapped once, or Caps _Lock_ if tapped twice. The expandability of the number of [`bindings`](#bindings) attached to a particular tap-dance is a great way to add more functionality to a single key, especially for keyboards with a limited number of keys. Tap-dances are completely custom, so for every unique tap-dance key,a new tap-dance must be defined in your keymap's `behaviors`.
+A tap-dance key invokes a different behavior (e.g. `kp`) corresponding to how many times it is pressed.
+For example, you could configure a tap-dance key that acts as `LSHIFT` if tapped once, or Caps _Lock_ if tapped twice.
+The expandability of the number of [`bindings`](#bindings) attached to a particular tap-dance is a great way to add more functionality to a single key, especially for keyboards with a limited number of keys.
+Tap-dances accept two parameters from your keymap, which you can map to the behaviors within as you please.
Tap-dances are designed to resolve immediately when interrupted by another keypress. Meaning, when a keybind is pressed other than any active tap-dances, the tap-dance will activate according to the current value of its counter before the interrupting keybind is registered.
-### Configuration
+## Keypress Tap Dance
-#### `tapping-term-ms`
+ZMK provides a simple two-tap tap dance for you to use. Press it once, and it will output a key press with the first parameter.
+Press it twice within 200ms, and it will output a key press with the second parameter.
+
+### Behavior Binding
+
+- Reference: `&td`
+- Parameters: The keycode usage IDs from the usage page, e.g. `N4` or `A`
+
+Example:
+
+```dts
+&td A B
+```
+
+## Configuration
+
+### `tapping-term-ms`
Defines the maximum elapsed time after the last tap-dance keybind press before a binding is selected from [`bindings`](#bindings). Default value is `200`ms.
-#### `bindings`
+### `bindings`
An array of one or more keybinds. This list can include [any ZMK keycode](../list-of-keycodes.mdx) and any listed ZMK behavior, like [hold-taps](hold-tap.mdx), or [sticky keys](sticky-key.md). The index of a keybind in the `bindings` array corresponds to the number of times the tap-dance binding is pressed. For example, in the basic tap-dance counter shown below, `&kp N2` is the second binding in the array of `bindings`: we then see an output of `2` when the `td0` binding is pressed twice.
The number of bindings in this array also determines the tap-dance's maximum number of keypresses. When a tap-dance reaches its maximum number of keypresses, it will immediately invoke the last behavior in its list of `bindings`, rather than waiting for [`tapping-term-ms`](#tapping-term-ms) to expire before the output is displayed.
-### Example Usage
+### `binding-params`
+
+The `binding-params` property determines how the parameters passed to the tap-dance behavior are passed on to the behaviors listed in `bindings`.
+It is an array of `BINDING_PARAM(arg1,arg2)` elements, one for each behavior in `bindings`.
+The Nth `BINDING_PARAM(arg1,arg2)` corresponds to the Nth behavior listed in `bindings`.
+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 tap-dance behavior received
+- `2`: The Xth parameter passed will be the second parameter that the tap-dance 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 = ;
+```
+
+The first behavior receives both input parameters, the second receives neither.
+
+```dts
+binding-params = ;
+```
+
+The first behavior receives the second input parameter as its first parameter, the second behavior receives the first input parameter as its first parameter, the third receives neither.
+
+```dts
+binding-params = ;
+```
+
+No behavior receives any input parameter. Note that tap-dance always requires two parameters to be passed, so you will still need to write e.g. `&my_td 0 0` in your keymap - the parameters passed will be ignored[^1].
+
+## Example Usage
;
+ compatible = "zmk,behavior-tap-dance-param";
+ #binding-cells = <2>;
tapping-term-ms = <200>;
bindings = <&kp N1>, <&kp N2>, <&kp N3>;
+ binding-params = ;
};
};
@@ -56,7 +109,7 @@ This example configures a tap-dance named `td0` that outputs the number of times
default_layer {
bindings = <
- &td0
+ &td0 0 0
>;
};
};
@@ -75,7 +128,7 @@ Alphanumeric [`key press`](key-press.md) bindings, like those used for `td0`, wi
-This example configures a mod-tap inside a tap-dance named `td_mt` that outputs `CAPSLOCK` on a single tap, `LSHIFT` on a single press and hold, and `LCTRL` when the tap-dance is pressed twice.
+This example configures a mod-tap inside a tap-dance named `td_mt` that outputs its first parameter on a single tap, `LSHIFT` on a single press and hold, and its second parameter when the tap-dance is pressed twice.
```dts title="Advanced Tap-Dance Example: Nested Mod-Tap"
#include
@@ -84,10 +137,11 @@ This example configures a mod-tap inside a tap-dance named `td_mt` that outputs
/ {
behaviors {
td_mt: tap_dance_mod_tap {
- compatible = "zmk,behavior-tap-dance";
- #binding-cells = <0>;
+ compatible = "zmk,behavior-tap-dance-param";
+ #binding-cells = <2>;
tapping-term-ms = <200>;
- bindings = <&mt LSHIFT CAPSLOCK>, <&kp LCTRL>;
+ bindings = <&mt LSHIFT PLACEHOLDER>, <&kp PLACEHOLDER>;
+ binding-params = ;
};
};
@@ -96,7 +150,7 @@ This example configures a mod-tap inside a tap-dance named `td_mt` that outputs
default_layer {
bindings = <
- &td_mt
+ &td_mt CAPSLOCK LCTRL
>;
};
};
@@ -105,3 +159,18 @@ This example configures a mod-tap inside a tap-dance named `td_mt` that outputs
+
+[^1]: There exists a simplified version of tap-dance without any input parameters, `compatible="zmk,behavior-tap-dance"`:
+
+```dts
+/ {
+ behaviors {
+ td0: tap_dance_0 {
+ compatible = "zmk,behavior-tap-dance";
+ #binding-cells = <0>;
+ tapping-term-ms = <200>;
+ bindings = <&kp N1>, <&kp N2>, <&kp N3>;
+ };
+ };
+};
+```