feat(docs): Improve parameterized macros docs

This commit is contained in:
Jorge Villalobos 2023-08-02 12:57:00 -04:00 committed by GitHub
parent e65a7e3075
commit d17c473d45
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
2 changed files with 122 additions and 65 deletions

View file

@ -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
= <&macro_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:
- `&macro_param_1to1` - pass the first parameter of the macro into the first parameter of the next behavior in the `bindings` list.
- `&macro_param_1to2` - pass the first parameter of the macro into the second parameter of the next behavior in the `bindings` list.
* `&macro_param_2to1` - pass the second parameter of the macro into the first parameter of the next behavior in the `bindings` list.
* `&macro_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 = <&macro_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:
- `&macro_param_1to1` - pass the first parameter of the macro into the first parameter of the next behavior in the `bindings` list.
- `&macro_param_1to2` - pass the first parameter of the macro into the second parameter of the next behavior in the `bindings` list.
* `&macro_param_2to1` - pass the second parameter of the macro into the first parameter of the next behavior in the `bindings` list.
* `&macro_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
= <&macro_press &mo 1 &kp LSHFT>
, <&macro_pause_for_release>
, <&macro_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
= <&macro_param_1to1>
, <&macro_press &mo MACRO_PLACEHOLDER>
, <&macro_param_2to1>
, <&macro_press &kp MACRO_PLACEHOLDER>
, <&macro_pause_for_release>
, <&macro_param_2to1>
, <&macro_release &kp MACRO_PLACEHOLDER>
, <&macro_param_1to1>
, <&macro_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>;

View file

@ -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**:<br/>`"zmk,behavior-macro"`<br/>`"zmk,behavior-macro-one-param"`<br/>`"zmk,behavior-macro-two-param"` | |
| `#binding-cells` | int | Number of params accepted (depends on `compatible` property), **must be _one_ of**:<br/>`<0>`<br/>`<1>`<br/>`<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 |
| -------------------------- | ----------------------------------------------------------------------------------------------------- |
| `&macro_tap` | Switches to tap mode |
| `&macro_press` | Switches to press mode |
| `&macro_release` | Switches to release mode |
| `&macro_pause_for_release` | Pauses the macro until the macro key itself is released |
| `&macro_wait_time TIME` | Changes the time to wait (in milliseconds) before triggering the next behavior. |
| `&macro_tap_time TIME` | Changes the time to wait (in milliseconds) between the press and release events of a tapped behavior. |
| Behavior | Description |
| -------------------------- | -------------------------------------------------------------------------------------------------------------------- |
| `&macro_tap` | Switches to tap mode |
| `&macro_press` | Switches to press mode |
| `&macro_release` | Switches to release mode |
| `&macro_pause_for_release` | Pauses the macro until the macro key itself is released |
| `&macro_wait_time TIME` | Changes the time to wait (in milliseconds) before triggering the next behavior. |
| `&macro_tap_time TIME` | Changes the time to wait (in milliseconds) between the press and release events of a tapped behavior. |
| `&macro_param_1to1` | Forward the first parameter received by the macro to the first parameter of the next (non-macro control) behavior. |
| `&macro_param_1to2` | Forward the first parameter received by the macro to the second parameter of the next (non-macro control) behavior. |
| `&macro_param_2to1` | Forward the second parameter received by the macro to the first parameter of the next (non-macro control) behavior. |
| `&macro_param_2to2` | Forward the second parameter received by the macro to the second parameter of the next (non-macro control) behavior. |
## Mod-Morph