From 5ba0a64e206e19b13fed6625c0c01720b11ec192 Mon Sep 17 00:00:00 2001 From: Nicolas Munnich Date: Thu, 19 Dec 2024 01:17:03 +0100 Subject: [PATCH] docs: Documented parameterised tap dance and mod morph --- docs/docs/config/behaviors.md | 52 +++++++---- docs/docs/keymaps/behaviors/mod-morph.md | 108 ++++++++++++++++------ docs/docs/keymaps/behaviors/tap-dance.mdx | 95 ++++++++++++++++--- 3 files changed, 199 insertions(+), 56 deletions(-) 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**: | +| `#binding-cells` | int | Must be | +| `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**: | | +| `#binding-cells` | int | Must be | | +| `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>; + }; + }; +}; +```