diff --git a/docs/docs/behaviors/macros.md b/docs/docs/behaviors/macros.md
index 279d1356..0757e735 100644
--- a/docs/docs/behaviors/macros.md
+++ b/docs/docs/behaviors/macros.md
@@ -49,22 +49,6 @@ For use cases involving sending a single keycode with modifiers, for instance ct
with [modifier functions](../codes/modifiers.mdx#modifier-functions) can be used instead of a macro.
:::
-### Parameterized Macros
-
-Macros can also be "parameterized", allowing them to be bound in your keymap with unique values passed into them, e.g.:
-
-```
- raise_layer {
- bindings = <&my_cool_macro A>
- };
-```
-
-When defining a parameterized macro, a different `compatible` value will be used depending on how many parameters are passed into it:
-
-- `zmk,behavior-macro` - a macro that takes no parameters.
-- `zmk,behavior-macro-one-param` - a macro that takes one parameter when used.
-- `zmk,behavior-macro-two-param` - a macro that takes two parameters when used.
-
### Bindings
Like [hold-taps](/docs/behaviors/hold-tap), macros are created by composing other behaviors, and any of those behaviors can
@@ -83,30 +67,6 @@ bindings
There are a set of special macro controls that can be included in the `bindings` list to modify the
way the macro is processed.
-### Parameters
-
-When creating a macro that takes parameter(s), there are macro controls that change when the parameters passed to the macro are used
-within the macro itself. All of the controls are "one shot" and will change how the passed in parameters are used for the very next non-macro control behavior in the `bindings` list of the macro.
-
-For example, to pass the first parameter from the macro into a `&kp` used in the macro, you would use:
-
-```
-bindings
- = <¯o_param_1to1>
- , <&kp MACRO_PLACEHOLDER>
- ;
-```
-
-Because `kp` takes one parameter, you can't simply make the second entry `<&kp>` in the `bindings` list. Whatever value you do pass in will be replaced when the macro is triggered, so you can put _any_ value there, e.g. `0`, `A` keycode, etc. To make it very obvious that the parameter there is not actually going to be used, you can use `MACRO_PLACEHOLDER` which is simply an alias for `0`.
-
-The available parameter controls are:
-
-- `¯o_param_1to1` - pass the first parameter of the macro into the first parameter of the next behavior in the `bindings` list.
-- `¯o_param_1to2` - pass the first parameter of the macro into the second parameter of the next behavior in the `bindings` list.
-
-* `¯o_param_2to1` - pass the second parameter of the macro into the first parameter of the next behavior in the `bindings` list.
-* `¯o_param_2to2` - pass the second parameter of the macro into the second parameter of the next behavior in the `bindings` list.
-
### Binding Activation Mode
Bindings in a macro are activated differently, depending on the current "activation mode" of the macro.
@@ -185,6 +145,70 @@ Macros use an internal queue to invoke each behavior in the bindings list when t
To prevent issues with longer macros, you can change the size of this queue via the `CONFIG_ZMK_BEHAVIORS_QUEUE_SIZE` setting in your configuration, [typically through your `.conf` file](../config/index.md). For example, `CONFIG_ZMK_BEHAVIORS_QUEUE_SIZE=512` would allow your macro to type about 256 characters.
+## Parameterized Macros
+
+Macros can also be "parameterized", allowing them to be bound in your keymap with unique values passed into them, e.g.:
+
+```
+ raise_layer {
+ bindings = <&my_one_param_macro A>
+ };
+```
+
+### Defining Parameterized Macros
+
+Parameterized macros must be defined using specific values for the `compatible` and `#binding-cells` properties, depending on how many parameters they require (up to a maximum of two):
+
+```dts
+/ {
+ macros {
+ // 0 params macro
+ my_macro: my_macro {
+ // ...
+ compatible = "zmk,behavior-macro";
+ #binding-cells = <0>; // Must be 0
+ bindings = /* ... */;
+ };
+
+ // 1 param macro
+ my_one_param_macro: my_one_param_macro {
+ // ...
+ compatible = "zmk,behavior-macro-one-param";
+ #binding-cells = <1>; // Must be 1
+ bindings = /* ... */;
+ };
+
+ // 2 params macro
+ my_two_param_macro: my_two_param_macro {
+ // ...
+ compatible = "zmk,behavior-macro-two-param";
+ #binding-cells = <2>; // Must be 2
+ bindings = /* ... */;
+ };
+ };
+};
+```
+
+### Parameters, Bindings and Controls
+
+There are special macro controls which must be used in order to forward received parameters to the macro's `bindings`. These controls are "one shot" and will determine how received parameters are used on the very next (non-macro control) behavior in the macro's `bindings` list.
+
+For example, to pass the first parameter received into a `&kp` binding, you would use:
+
+```dts
+bindings = <¯o_param_1to1>, <&kp MACRO_PLACEHOLDER>;
+```
+
+Because `kp` takes one parameter, you can't simply make the second entry `<&kp>` in the `bindings` list. Whatever value you do pass in will be replaced when the macro is triggered, so you can put _any_ value there, e.g. `0`, `A` keycode, etc. To make it very obvious that the parameter there is not actually going to be used, you can use `MACRO_PLACEHOLDER` which is simply an alias for `0`.
+
+The available parameter controls are:
+
+- `¯o_param_1to1` - pass the first parameter of the macro into the first parameter of the next behavior in the `bindings` list.
+- `¯o_param_1to2` - pass the first parameter of the macro into the second parameter of the next behavior in the `bindings` list.
+
+* `¯o_param_2to1` - pass the second parameter of the macro into the first parameter of the next behavior in the `bindings` list.
+* `¯o_param_2to2` - pass the second parameter of the macro into the second parameter of the next behavior in the `bindings` list.
+
## Common Patterns
Below are some examples of how the macro behavior can be used for various useful functionality.
@@ -198,12 +222,36 @@ To achieve this, a combination of a 0ms wait time and splitting the press and re
#### Layer + Modifier
-```
-wait-ms = <0>;
-bindings
- = <¯o_press &mo 1 &kp LSHFT>
- , <¯o_pause_for_release>
- , <¯o_release &mo 1 &kp LSHFT>;
+```dts
+/**
+ * Temporarily switches to a layer (`&mo`) while a modifier is held.
+ * Analogous to QMK's `LM()`, using a parameterized macro.
+ *
+ * Params:
+ * 1. Layer to switch to
+ * 2. Modifier to press while layer is active
+ *
+ * Example:
+ * `&lm NUM_LAYER LSHIFT`
+ */
+lm: lm {
+ label = "LAYER_MOD";
+ compatible = "zmk,behavior-macro-two-param";
+ wait-ms = <0>;
+ tap-ms = <0>;
+ #binding-cells = <2>;
+ bindings
+ = <¯o_param_1to1>
+ , <¯o_press &mo MACRO_PLACEHOLDER>
+ , <¯o_param_2to1>
+ , <¯o_press &kp MACRO_PLACEHOLDER>
+ , <¯o_pause_for_release>
+ , <¯o_param_2to1>
+ , <¯o_release &kp MACRO_PLACEHOLDER>
+ , <¯o_param_1to1>
+ , <¯o_release &mo MACRO_PLACEHOLDER>
+ ;
+};
```
#### Layer + Underglow Color
@@ -252,20 +300,24 @@ bindings
## Convenience C Macro
-To avoid repetition or possible typos when declaring a macro, a convenience _C_ macro, named `ZMK_MACRO(name, props)` can be used to simplify things:
+To avoid repetition or possible typos when declaring a **zero parameter macro**, a convenience _C_ macro, named `ZMK_MACRO(name, props)` can be used to simplify things:
```
- ZMK_MACRO(my_macro,
+ ZMK_MACRO(my_zero_param_macro,
wait-ms = <30>;
tap-ms = <40>;
bindings = <&kp Z &kp M &kp K>;
)
```
+:::note
+`ZMK_MACRO()` **only supports declaring non-parameterized (zero parameter) macros**; parameterized declarations are not currently supported.
+:::
+
This can be used instead of a complete macro definition. During the firmware build process, the example above would produce the complete macro definition below:
```
- my_macro: my_macro {
+ my_zero_param_macro: my_zero_param_macro {
compatible = "zmk,behavior-macro";
label = "ZM_my_macro";
#binding-cells = <0>;
diff --git a/docs/docs/config/behaviors.md b/docs/docs/config/behaviors.md
index 2ff99d58..60e8b72a 100644
--- a/docs/docs/config/behaviors.md
+++ b/docs/docs/config/behaviors.md
@@ -131,26 +131,31 @@ See the [macro behavior](../behaviors/macros.md) documentation for more details
Definition file: [zmk/app/dts/bindings/behaviors/zmk,behavior-macro.yaml](https://github.com/zmkfirmware/zmk/blob/main/app/dts/bindings/behaviors/zmk%2Cbehavior-macro.yaml)
-Applies to: `compatible = "zmk,behavior-macro"`
+| Property | Type | Description | Default |
+| ---------------- | ------------- | -------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------- |
+| `label` | string | Unique label for the node | |
+| `compatible` | string | Macro type, **must be _one_ of**:
• `"zmk,behavior-macro"`
• `"zmk,behavior-macro-one-param"`
• `"zmk,behavior-macro-two-param"` | |
+| `#binding-cells` | int | Number of params accepted (depends on `compatible` property), **must be _one_ of**:
• `<0>`
• `<1>`
• `<2>` | |
+| `bindings` | phandle array | List of behaviors to trigger | |
+| `wait-ms` | int | The default time to wait (in milliseconds) before triggering the next behavior. | `CONFIG_ZMK_MACRO_DEFAULT_WAIT_MS` |
+| `tap-ms` | int | The default time to wait (in milliseconds) between the press and release events of a tapped behavior. | `CONFIG_ZMK_MACRO_DEFAULT_TAP_MS` |
-| Property | Type | Description | Default |
-| ---------------- | ------------- | ----------------------------------------------------------------------------------------------------- | ---------------------------------- |
-| `label` | string | Unique label for the node | |
-| `#binding-cells` | int | Must be `<0>` | |
-| `bindings` | phandle array | List of behaviors to trigger | |
-| `wait-ms` | int | The default time to wait (in milliseconds) before triggering the next behavior. | `CONFIG_ZMK_MACRO_DEFAULT_WAIT_MS` |
-| `tap-ms` | int | The default time to wait (in milliseconds) between the press and release events of a tapped behavior. | `CONFIG_ZMK_MACRO_DEFAULT_TAP_MS` |
+### Macro Control Behaviors
The following macro-specific behaviors can be added at any point in the `bindings` list to change how the macro triggers subsequent behaviors.
-| Behavior | Description |
-| -------------------------- | ----------------------------------------------------------------------------------------------------- |
-| `¯o_tap` | Switches to tap mode |
-| `¯o_press` | Switches to press mode |
-| `¯o_release` | Switches to release mode |
-| `¯o_pause_for_release` | Pauses the macro until the macro key itself is released |
-| `¯o_wait_time TIME` | Changes the time to wait (in milliseconds) before triggering the next behavior. |
-| `¯o_tap_time TIME` | Changes the time to wait (in milliseconds) between the press and release events of a tapped behavior. |
+| Behavior | Description |
+| -------------------------- | -------------------------------------------------------------------------------------------------------------------- |
+| `¯o_tap` | Switches to tap mode |
+| `¯o_press` | Switches to press mode |
+| `¯o_release` | Switches to release mode |
+| `¯o_pause_for_release` | Pauses the macro until the macro key itself is released |
+| `¯o_wait_time TIME` | Changes the time to wait (in milliseconds) before triggering the next behavior. |
+| `¯o_tap_time TIME` | Changes the time to wait (in milliseconds) between the press and release events of a tapped behavior. |
+| `¯o_param_1to1` | Forward the first parameter received by the macro to the first parameter of the next (non-macro control) behavior. |
+| `¯o_param_1to2` | Forward the first parameter received by the macro to the second parameter of the next (non-macro control) behavior. |
+| `¯o_param_2to1` | Forward the second parameter received by the macro to the first parameter of the next (non-macro control) behavior. |
+| `¯o_param_2to2` | Forward the second parameter received by the macro to the second parameter of the next (non-macro control) behavior. |
## Mod-Morph