Keyboard Macros
RMK supports keyboard macros: Pressing a trigger to execute a sequence of keypresses.
This can be configured via Vial, the toml configuration file, or Rust.
Macro operations
The following operations, coming from Vial, can be used to form a macro sequence. They are in rmk::keyboard_macros:
Text(HidKeyCode, bool)
Execute a key press from any available HID keycode. The boolean flags if the key should be pressed with the shift modifier.
Note that other modifiers pressed outside of a sequence with Text are disabled.
Tap(HidKeyCode)
Presses and releases a key. Modifiers pressed outside of a macro sequence are considered as well. If you don't need this prefer Text(HidKeyCode, bool) above, as the resulting macro is 3 times smaller in size.
Press(HidKeyCode)
Press (and hold) a keycode. Useful for modifier keys.
Release(HidKeyCode)
Release (a formerly pressed) keycode. Useful for modifier keys.
Delay(u16)
Wait the given time in ms before executing the next macro operation.
End
This marks the end of a macro sequence. Don't use it: The code removes all occurrences and adds one marker to the end of every sequence to be sure the sequences are terminated correctly.
With the vial feature enabled there are additionally TapAction, PressAction, and ReleaseAction operations, which tap/press/release an extended (non-HID) Action such as a Bluetooth-profile key.
Configure a macro sequence
Via the configuration file
See macro section under behavior
Via Rust
A new field keyboard_macros has been added to the BehaviorConfig struct. Within it a field macro_sequences has to be set. This is in binary format ([u8]) and can only be as long as MACRO_SPACE_SIZE, which defaults to 256 and can be changed via macro_space_size in the [rmk] section of keyboard.toml.
The maximum number of Macros depends on the length of the sequences. Each Text operation takes 1 byte, Tap/Press/Release take 3 bytes, Delay and TapAction/PressAction/ReleaseAction take 4 bytes, and every macro ends with a 1-byte terminator.
If your sequences don't fit into MACRO_SPACE_SIZE, define_macro_sequences panics with "Too many Macro Operations!".
There are two helper functions to define macro sequences:
define_macro_sequences(&[heapless::Vec<MacroOperation, MACRO_SPACE_SIZE>])You can use it this way:
This code defines two macro sequences which produce "Hello" and "World". (As mentioned above prefer the first Macro for text only output. The first macro sequence is 6 bytes long, the second 22 bytes.)
For text output there is a convenience function: to_macro_sequence(text: &str) -> heapless::Vec<MacroOperation, MACRO_SPACE_SIZE>.
This function converts a &str into a sequence of MacroOperation::Text. The above example would be:
(With the improvement that the Text macro operation is used in both cases.)
Note that you are still limited to the ascii characters defined as HidKeyCodes. For example, you can't enter a German Umlaut (ü) or unicode directly with a HidKeyCode binding. If you enter an illegal character it will be converted to No (nothing is typed).
Entering these special characters usually require a key combination which depends on your operating system and chosen keyboard layout (setting in the OS). For example, in MacOS with a en-US layout you can define the following sequence to enter an ö:
Triggering a macro
Binding
A macro can be triggered in two ways:
- Using the macro shortcuts in keymap configuration (e.g.,
Macro(0)-Macro(255)in toml config, ormacros!(0)-macros!(255)in Rust). - Using the
Action::TriggerMacro(index), where index can be any number between 0~255. If the total number of macro sequences is less than the index passed, nothing is executed (and an error "Macro not found" is logged). Remember that the index starts at0. - Defined macro sequences are automatically bound to a sequence: The first macro sequence defined is executed when triggering
Macro(0)andAction::TriggerMacro(0).
There is no difference using either: macros!(n) simply expands to KeyAction::Single(Action::TriggerMacro(n)).
Combining
Both macro triggers can be used anywhere, where a KeyCode or an Action can be assigned.
As the only Action taking a KeyCode is Action::Key, combining with Actions is limited.
With KeyAction
You can combine the trigger with any KeyAction, like layer-taps, hold-taps, etc.
For example:
Probably you most likely will need
or
With Combo (chording)
Combining with Combo allows for a quite powerful feature: Chording. Chording comes for the courtroom stenography and has its name from playing chords, like on a guitar. Chording is pressing a few letters to emit multiple letters.
Thus, one can press only the beginning of a word to write the whole word. For example, pressing T & Y could write type, pressing T & Y& G could write typing. If you want to implement this behavior we recommend using an extra layer, so rolling over T and Y will not accidentally execute the macro, but only when a layer toggle key is pressed as well.
This is the configuration for the above example, assuming 1 is the chording layer:
(Action::TriggerMacro(1) was used for demonstration only. Using macros!(1) is recommended to keep it brief.)
Note that instead of having a second macro for all verbs (normal and ing form) you can define a macro which converts a word to the ing form:
With the configuration above pressing T & Y writes type and pressing G changes it to typing.
With forks
You can use macro triggers in forks as well.
This is how you can trigger hello and Hello with pressing shift:
Tips
Small and capital version of a word
If you want to spell a macro in small letters, but occationally with the first letter capitalized, you can do so in the following way:
For example, you might want to use a combo for the rare letter q. And as this letter mostly comes as qu you want to use a macro for that.
Thus, implement the macro:
When you press shift and use MacroOperation::Text, like in the code above, no letter gets capitalized (outputs qu). Remember that MacroOperation::Text ignores all modifiers not being part of the sequence. MacroOperation::Tap doesn't, thus you can use MacroOperation::Tap for the first letter, and MacroOperation::Text for the following letters, to capitalize the first letter only.