-
-
Notifications
You must be signed in to change notification settings - Fork 2.9k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat(behaviors): Add macro-pause-for-layer behavior #1804
base: main
Are you sure you want to change the base?
feat(behaviors): Add macro-pause-for-layer behavior #1804
Conversation
d78c1c7
to
83b9a01
Compare
83b9a01
to
13fbcb1
Compare
docs/docs/behaviors/macros.md
Outdated
@@ -109,6 +109,23 @@ bindings | |||
; | |||
``` | |||
|
|||
### Processing Continuation on Layer Change | |||
|
|||
The macro can also be paused so that part of the `bindings` list is processed when the layer changes. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think being more specific here will be nice: is it watching for the layer that the macro is on to be turned off? Or any change in the currently active set of layers?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Right now it's the latter: any change in the active set of layers will release all scheduled states. The former could be nice, but it would add some complexity to allow for storing scheduled states from different layers and releasing only some of them at a time.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Question: if I wanted to get the layer the macro was in, would I store the zmk_behavior_binding_event->layer
int passed into on_macro_binding_pressed()
?
And then to check if the layer is turned off in macro_layer_state_changed_listener()
, see if any bit in the stored layer
int has been switched off?
Edit: never mind, I figured it out.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I added a commit that enables the first behavior you suggested - it now watches for the layer the macro is on to be turned off. I also edited the documentation to be more specific.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think that's intuitive but either way could have been fine as long as it is documented.
One thing I am curious is how it behaves when the macro is not invoked directly from a layer binding, like
- a combo
- nested in another behavior (hold-tap, mod-morph, another macro etc.)
I am not sure it needs to work with combos since it doesn't make much sense, but maybe a warning note will be warranted.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, I added that behavior because I thought it would be more intuitive as well, so that adding another layer wouldn't trigger all continuations. However, it does limit macro_pause_for_layer
to be used only on non-default layers.
I just tested with a combo and it seems to count as layer 0 even if a different layer is active at the time, so it would not work with the current behavior.
Perhaps one way a combo would make sense is where app switch could be repeatedly activated with a combo in the base layer, and momentarily entering any other layer would then exit the app switcher. Not the way I'm used to using Alt+Tab, but perhaps useful for someone who is averse to holding multiple keys.
This use would require going back to the original behavior of continuing on any layer change, rather than waiting for the current layer's deactivation.
13f879f
to
28ca97f
Compare
I've rebased the PR on main, including support for parameterized macros (#1232). In order to support parameterized macros, as well as the |
28ca97f
to
db1de28
Compare
Adding another force-push for the deprecated label property refactor (#2028). |
db1de28
to
4ac90dd
Compare
Description
This PR adds a
macro_pause_for_layer
behavior that pauses the current macro and resumes it on the next layer change.Motivation
The callum layout has a "swap win" key that sends Cmd+Tab and holds Cmd between presses.
In ZMK, binding the "swap win" key to
LG(TAB)
only allows swapping between the top two windows, as Cmd is released between presses.Solution
The
macro_pause_for_layer
behavior allows "swap win" to be bound to a macro that presses Cmd, taps Tab, then waits for the Nav layer to change before releasing Cmd. With this binding, Nav+"swap win" behaves similarly to Cmd+Tab on a conventional keyboard.Unlike
on_macro_binding_released()
,macro_layer_state_changed_listener()
does not receive the current binding to resume. Therefore, themacro_pause_for_layer
behavior instead saves a queue of scheduled macro states to resume (1 for each time the macro is pressed). Then, when the layer changes, all of these states are resumed. The queue size is16 * 8 = 128currently 16 * 20 = 320 bytes allocated statically.If the queue size limit is reached, any subsequent
¯o_pause_for_layer
s are bypassed and their macros processed immediately in full. This isn't an issue for the Cmd+Tab example as the number ofmacro_release
calls equals the number ofmacro_press
calls, and Cmd retains somemacro_press
calls until the layer changes and all the queuedmacro_release
calls are executed.Potential improvements
I wonder if(now added)behavior_macro_scheduled_state
can get away with storingchar*
pointers for eachbehavior_dev
? This would require less memory, simplify the code, and also prevent issues with truncated labels.macro_pause_for_release
andmacro_pause_for_layer
cannot be used in the same macro, but that should be possible if a separaterelease_state
is kept for each trigger.