Skip to content

Using Parameterized Macros in Keymap Editor

Nick Coutsos edited this page Oct 30, 2023 · 1 revision

If you aren't already familiar with macros in the context of ZMK, they are a behavior type that allows you to define a sequence of bindings to be triggered wherever that macro is bound. Typically this is to a key in one of your keymap's layers, but it can also be a combo, a behavior, or another macro.

Not too long ago support for parameterized macros landed in ZMK, so in addition to enabling a quick way to trigger a number of behaviors you can supply parameters to specific bindings in that sequence for added flexibility and reuse.

Use Case: Per-layer RGB lighting

In another post I detailed a combination of parameterized macros and hold tap to implement autoshift functionality without repetition in your bindings or resorting to preprocessor macros. Now I want to demonstrate the advanced support in Keymap Editor for resolving parameter types, even in the context of a macro.

Example from ZMK Docs

In the ZMK macro docs there is an example Layer + Underglow Color macro. It uses the &macro_press, &macro_tap, &macro_pause_for_release, and &macro_release binding activation modes to:

  1. "hold" a &mo (momentary layer) binding
  2. "tap" a &rgb_ug (colour change) binding
  3. pause as long as the macro is held,
  4. "release" the initial &mo binding
  5. "tap" another &rgb_ug (presumably default colour) binding

By turning this into a parameterized macro, the same behavior can be used for any layer without having to create a duplicate.

Creating the macro

In Keymap Editor load your keymap and go to the Macros tab. Add a new macro called &rgblayer.

Screenshot from the Keymap Editor's new macro dialog

From here you can start adding control and key bindings to recreate the original macro:

Screenshot from the Keymap Editor's macro binding editor showing a sequence of bindings similar to the "Layer + Underglow Color macro" example

Note: the macro sequence editor is drag-and-drop, so if you forget to add a binding in the correct spot you can always reorder them afterwards.

Parameterizing

Now to make this generic, we'll include the appropriate control bindings to map macro parameters into the &mo and &rgb_ug bindings. Click the Add Control Binding and then change its behavior to &macro_param_1to1.

Screen Shot of the macro binding editor showing the warning "Parameter assignment has no affected binding"

Note that as soon as this new binding is added to the end of the sequence you'll get a warning that there's no binding following it to receive the parameter. Drag the binding above the first &mo 1 and you'll see the warning disappear as well as the layer value.

The editor knows that &macro_param_1to1 will cause the next non-control binding to receive the first parameter passed to the macro, in this case that binding is &mo, so the given value is replaced with ?? (in the generated code this will become MACRO_PLACEHOLDER).

Now you can add the rest:

  • &macro_param_2to2 before the first &rgb_ug binding
  • &macro_param_1to1 before the second &mo binding

Screen Shot of the completed macro binding sequence with parameters

Behind the scenes

If you've loaded a keymap using the Clipboard integration, you can click the Keymap button and view macro source generated as something like the following:

    rgblayer: rgb_layer {
      compatible = "zmk,behavior-macro-two-param";
      #binding-cells = <2>;
      label = "MOMENTARY_LAYER_WITH_RGB_COLOR";
      bindings
        = <&macro_param_2to2 &rgb_ug RGB_COLOR_HSB_CMD MACRO_PLACEHOLDER &macro_param_1to1>
        , <&macro_press>
        , <&mo MACRO_PLACEHOLDER>
        , <&macro_pause_for_release>
        , <&macro_release>
        , <&macro_param_1to1 &mo MACRO_PLACEHOLDER>
        , <&macro_tap>
        , <&rgb_ug RGB_COLOR_HSB(0, 0, 0)>
        ;
    };

There are a few things to note:

  1. The behavior type zmk,behavior-macro-two-param is selected for you, because you've indicated the use of two parameters.
  2. The identifier MACRO_PLACEHOLDER is injected for you -- it's value doesn't matter, but it helps to make it clear what the purpose is.
  3. Although you may be used to &rgb_ug RGB_COLOR_HSB(...) this is a preprocessor function that produces two values: RGB_COLOR_HSB_CMD and RGB_COLOR_HSB_VAL(...). The Keymap Editor recognizes the relationship here, and splits the binding up once it's identified as receiving a macro parameter.

Using the macro

Anywhere you might like to momentarily activate a layer you can change that binding's &mo behavior to &rgblayer. In the binding editor you'll see something like the following:

Screen Shot of the binding editor dialog with the "&rgblayer" macro behavior selected. The "layer" and "RGB Underglow Color HSB" parameter types are displayed

The parameter type resolution support built into Keymap Editor recognizes that:

  1. the macro accepts two parameters,
  2. the &mo binding has a placeholder for its first parameter, a layer
  3. the rgb_ug binding has a placeholder for its second parameter, and because its first parameter is already set as RGB_COLOR_HSB_CMD the second must therefore be RGB_COLOR_HSB_VAL(...) which is already set up to use the colour picker helper.