Peter Johanson
3 years ago
committed by
Pete Johanson
3 changed files with 229 additions and 1 deletions
@ -0,0 +1,227 @@ |
|||||||
|
--- |
||||||
|
title: Macro Behavior |
||||||
|
sidebar_label: Macros |
||||||
|
--- |
||||||
|
|
||||||
|
## Summary |
||||||
|
|
||||||
|
The macro behavior allows configuring a list of other behaviors to invoke |
||||||
|
when the macro is pressed and/or released. |
||||||
|
|
||||||
|
## Macro Definition |
||||||
|
|
||||||
|
Each macro you want to use in your keymap gets defined first, then bound in your keymap. |
||||||
|
|
||||||
|
A macro definition looks like: |
||||||
|
|
||||||
|
``` |
||||||
|
/ { |
||||||
|
macros { |
||||||
|
zed_em_kay: zed_em_kay { |
||||||
|
label = "ZM_zed_em_kay"; |
||||||
|
compatible = "zmk,behavior-macro"; |
||||||
|
#binding-cells = <0>; |
||||||
|
bindings |
||||||
|
= <¯o_press &kp LSHFT> |
||||||
|
, <¯o_tap &kp Z &kp M &kp K> |
||||||
|
, <¯o_release &kp LSHFT> |
||||||
|
; |
||||||
|
}; |
||||||
|
}; |
||||||
|
}; |
||||||
|
``` |
||||||
|
|
||||||
|
:::note |
||||||
|
The text before the colon (`:`) in the declaration of the macro node is the "node label", and is the text |
||||||
|
used to reference the macro in your keymap |
||||||
|
::: |
||||||
|
|
||||||
|
The macro can then be bound in your keymap by referencing it by the label `&zed_em_kay`, e.g.: |
||||||
|
|
||||||
|
``` |
||||||
|
raise_layer { |
||||||
|
bindings = <&zed_em_kay>; |
||||||
|
}; |
||||||
|
``` |
||||||
|
|
||||||
|
### Bindings |
||||||
|
|
||||||
|
Like [hold-taps](/docs/behaviors/hold-tap), macros are created by composing other behaviors, and any of those behaviors can |
||||||
|
be added to the `bindings` list, e.g.: |
||||||
|
|
||||||
|
``` |
||||||
|
bindings |
||||||
|
= <&to 1> |
||||||
|
, <&bl BL_ON> |
||||||
|
, <&kp Z &kp M &kp K &kp EXCLAMATION> |
||||||
|
; |
||||||
|
``` |
||||||
|
|
||||||
|
## Macro Controls |
||||||
|
|
||||||
|
There are a set of special macro controls that can be included in the `bindings` list to modify the |
||||||
|
way the macro is processed. |
||||||
|
|
||||||
|
### Binding Activation Mode |
||||||
|
|
||||||
|
Bindings in a macro are activated differently, depending on the current "activation mode" of the macro. |
||||||
|
|
||||||
|
Available modes: |
||||||
|
|
||||||
|
- Tap - The default mode; when in this mode, the macro will press, then release, each behavior in the `bindings` list. This mode is useful for |
||||||
|
basic keycode output to hosts, i.e. when activating a `&kp` behavior. |
||||||
|
- Press - In this mode, the macro will only trigger a press on each behavior in the `bindings` list. This is useful for holding down modifiers for some duration of a macro, e.g. `&kp LALT`. |
||||||
|
- Release - In this mode, the macro will only trigger a release on each behavior in the `bindings` list. This is useful for releasing modifiers previously pressed earlier in the macro processing, e.g. `&kp LALT`. |
||||||
|
|
||||||
|
To modify the activation mode, macro controls can be added at any point in the `bindings` list. |
||||||
|
|
||||||
|
- `¯o_tap` |
||||||
|
- `¯o_press` |
||||||
|
- `¯o_release` |
||||||
|
|
||||||
|
A concrete example, used to hold a modifier, tap multiple keys, then release the modifier, would look like: |
||||||
|
|
||||||
|
``` |
||||||
|
bindings |
||||||
|
= <¯o_press &kp LSHFT> |
||||||
|
, <¯o_tap &kp Z &kp M &kp K> |
||||||
|
, <¯o_release &kp LSHFT> |
||||||
|
; |
||||||
|
``` |
||||||
|
|
||||||
|
### Processing Continuation on Release |
||||||
|
|
||||||
|
The macro can be paused so that only part of the `bindings` list is processed when the macro is pressed, and the remainder is processed once |
||||||
|
the macro itself is released. |
||||||
|
|
||||||
|
To pause the macro until release, use `¯o_pause_for_release`. For example, this macro will press a modifier and activate a layer when the macro is pressed. Once the macro is released, it will release the modifier and deactivate the layer by releasing the `&mo`: |
||||||
|
|
||||||
|
``` |
||||||
|
bindings |
||||||
|
= <¯o_press &mo 1 &kp LSHFT> |
||||||
|
, <¯o_pause_for_release> |
||||||
|
, <¯o_release &mo 1 &kp LSHFT> |
||||||
|
; |
||||||
|
``` |
||||||
|
|
||||||
|
### Wait Time |
||||||
|
|
||||||
|
The wait time setting controls how long of a delay is introduced between behaviors in the `bindings` list. The initial wait time for a macro, 100ms by default, can |
||||||
|
be set by assigning a value to the `wait-ms` property of the macro, e.g. `wait-ms = <20>;`. If you want to update the wait time at any |
||||||
|
point in the macro bindings list, use `¯o_wait_time`, e.g. `¯o_wait_time 30`. A full example: |
||||||
|
|
||||||
|
``` |
||||||
|
wait-ms = <10>; |
||||||
|
bindings |
||||||
|
= <&kp F &kp A &kp S &kp T> |
||||||
|
, <¯o_wait_time 500> |
||||||
|
, <&kp S &kp L &kp O &kp W> |
||||||
|
; |
||||||
|
``` |
||||||
|
|
||||||
|
### Tap Time |
||||||
|
|
||||||
|
The tap time setting controls how long a tapped behavior is held in the `bindings` list. The initial tap time for a macro, 100ms by default, can |
||||||
|
be set by assigning a value to the `tap-ms` property of the macro, e.g. `tap-ms = <20>;`. If you want to update the tap time at any |
||||||
|
point in a macro bindings list, use `¯o_tap_time`, e.g. `¯o_tap_time 30`. A full example: |
||||||
|
|
||||||
|
``` |
||||||
|
bindings |
||||||
|
= <¯o_tap_time 10> |
||||||
|
, <&kp S &kp H &kp O &kp R &kp T> |
||||||
|
, <¯o_tap_time 500> |
||||||
|
, <&kp L &kp O &kp N &kp G> |
||||||
|
; |
||||||
|
``` |
||||||
|
|
||||||
|
## Common Patterns |
||||||
|
|
||||||
|
Below are some examples of how the macro behavior can be used for various useful functionality. |
||||||
|
|
||||||
|
### Layer Activation + More |
||||||
|
|
||||||
|
Macros make it easy to combine a [layer behavior](/docs/behaviors/layers), e.g. `&mo` with another behavior at the same time. |
||||||
|
Common examples are enabling one or more modifiers when the layer is active, or changing the RBG underglow color. |
||||||
|
|
||||||
|
To achieve this, a combination of a 0ms wait time and splitting the press and release between a `¯o_pause_for_release` is used: |
||||||
|
|
||||||
|
#### Layer + Modifier |
||||||
|
|
||||||
|
``` |
||||||
|
wait-ms = <0>; |
||||||
|
bindings |
||||||
|
= <¯o_press &mo 1 &kp LSHFT> |
||||||
|
, <¯o_pause_for_release> |
||||||
|
, <¯o_release &mo 1 &kp LSHFT>; |
||||||
|
``` |
||||||
|
|
||||||
|
#### Layer + Underglow Color |
||||||
|
|
||||||
|
To trigger a different underglow when the macro is pressed, and when it is released, we use the macro "press" activation mode whenever triggering the `&rgb_ug` behavior: |
||||||
|
|
||||||
|
``` |
||||||
|
wait-ms = <0>; |
||||||
|
tap-ms = <0>; |
||||||
|
bindings |
||||||
|
= <¯o_press &mo 1> |
||||||
|
, <¯o_tap &rgb_ug RGB_COLOR_HSB(128,100,100)> |
||||||
|
, <¯o_pause_for_release> |
||||||
|
, <¯o_release &mo 1> |
||||||
|
, <¯o_tap &rgb_ug RGB_COLOR_HSB(300,100,50)>; |
||||||
|
``` |
||||||
|
|
||||||
|
### Keycode Sequences |
||||||
|
|
||||||
|
The other common use case for macros is to sending sequences of keycodes to the connected host. Here, a wait and tap time of at least 30ms is recommended to |
||||||
|
avoid having HID notifications grouped at the BLE protocol level and then processed out of order: |
||||||
|
|
||||||
|
``` |
||||||
|
wait-ms = <40>; |
||||||
|
tap-ms = <40>; |
||||||
|
bindings |
||||||
|
= <&kp Z &kp M &kp K> |
||||||
|
, <&kp SPACE> |
||||||
|
, <&kp R &kp O &kp C &kp K &kp S> |
||||||
|
; |
||||||
|
``` |
||||||
|
|
||||||
|
### Unicode Sequences |
||||||
|
|
||||||
|
Many operating systems allow a special sequence to input unicode characters, e.g. [Windows alt codes](https://support.microsoft.com/en-us/office/insert-ascii-or-unicode-latin-based-symbols-and-characters-d13f58d3-7bcb-44a7-a4d5-972ee12e50e0). You can use macros to insert there automatically, e.g.: |
||||||
|
|
||||||
|
``` |
||||||
|
wait-ms = <40>; |
||||||
|
tap-ms = <40>; |
||||||
|
bindings |
||||||
|
= <¯o_press &kp LALT> |
||||||
|
, <¯o_tap &kp N0 &kp N1 &kp N6 &kp N3> |
||||||
|
, <¯o_release &kp LALT> |
||||||
|
; |
||||||
|
``` |
||||||
|
|
||||||
|
## 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: |
||||||
|
|
||||||
|
``` |
||||||
|
ZMK_MACRO(my_macro, |
||||||
|
wait-ms = <30>; |
||||||
|
tap-ms = <40>; |
||||||
|
bindings = <&kp Z &kp M &kp K>; |
||||||
|
) |
||||||
|
``` |
||||||
|
|
||||||
|
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 { |
||||||
|
compatible = "zmk,behavior-macro"; |
||||||
|
label = "ZM_my_macro"; |
||||||
|
#binding-cells = <0>; |
||||||
|
wait-ms = <30>; |
||||||
|
tap-ms = <40>; |
||||||
|
bindings = <&kp Z &kp M &kp K>; |
||||||
|
}; |
||||||
|
``` |
||||||
|
|
||||||
|
Using the C macro is entirely optional, and is provided only as a convenience. |
Loading…
Reference in new issue