-
-
Notifications
You must be signed in to change notification settings - Fork 2.8k
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): Support parameterized macros. #1232
feat(behaviors): Support parameterized macros. #1232
Conversation
The most likely scenario I can think where this could cause an issue is if a behavior packs multiple values into one parameter such that 0xFFFF is a valid and common value, for example |
Copying over my suggestion from Discord so it doesn't get lost: What if instead of having a special parameter value that gets replaced, we add a macro-specific behavior that modifies the following binding? For example, we could have something like
|
7b159f0
to
8ec44e1
Compare
I dig this idea. Implementation seemed simple enough. I opted for I also left a define From the test:
Thoughts? |
8ec44e1
to
6df23ef
Compare
@joelspadin Rebased onto the new Zephyr 3.2 |
Used to make a dynamic CTRL+KEY macro for easy use with a tap_hold key. No weird behaviors as everything seems to be working just fine, allowing me to hold keys like V ( Actual Configuration
|
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.
Needs docs updates, but the implementation looks good to me.
app/src/behaviors/behavior_macro.c
Outdated
|
||
LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL); | ||
|
||
#if DT_HAS_COMPAT_STATUS_OKAY(DT_DRV_COMPAT) | ||
#if DT_HAS_COMPAT_STATUS_OKAY(zmk_behavior_macro) || \ |
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.
Could this be simplified using Kconfig + CMake, or would you rather do that in a separate commit?
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.
Yes, it can be! Rebased onto main
where we have the Kconfig.behaviors
and tweaked this to use the DT_HAS defines and simplify the code by only compiling if we have any of the macro DT compatibles enabled.
app/src/behaviors/behavior_macro.c
Outdated
switch (state->param1_source) { | ||
case PARAM_SOURCE_MACRO_1ST: | ||
binding->param1 = macro_binding->param1; | ||
break; | ||
case PARAM_SOURCE_MACRO_2ND: | ||
binding->param1 = macro_binding->param2; | ||
break; | ||
default: | ||
break; | ||
} | ||
|
||
switch (state->param2_source) { | ||
case PARAM_SOURCE_MACRO_1ST: | ||
binding->param2 = macro_binding->param1; | ||
break; | ||
case PARAM_SOURCE_MACRO_2ND: | ||
binding->param2 = macro_binding->param2; | ||
break; | ||
default: | ||
break; | ||
} |
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.
This could be deduplicated with something like
update_param(&binding->param1, state->param1_source, ¯o_binding);
update_param(&binding->param2, state->param2_source, ¯o_binding);
Or more functional programming style:
binding->param1 = update_param(state->param1_source, binding->param1, ¯o_binding);
binding->param2 = update_param(state->param2_source, binding->param2, ¯o_binding);
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.
Good catch. Used the more functional style.
a48ffa9
to
8a0c9b5
Compare
app/src/behaviors/behavior_macro.c
Outdated
#define TRANSFORMED_BEHAVIORS(n) \ | ||
{LISTIFY(DT_PROP_LEN(DT_DRV_INST(n), bindings), BINDING_WITH_COMMA, (, ), n)}, | ||
{LISTIFY(DT_PROP_LEN(n, bindings), ZMK_KEYMAP_EXTRACT_BINDING, (, ), n)}, | ||
|
||
#define MACRO_INST(n) \ | ||
static struct behavior_macro_state behavior_macro_state_##n = {}; \ | ||
static struct behavior_macro_config behavior_macro_config_##n = { \ | ||
.default_wait_ms = DT_INST_PROP_OR(n, wait_ms, CONFIG_ZMK_MACRO_DEFAULT_WAIT_MS), \ | ||
.default_tap_ms = DT_INST_PROP_OR(n, tap_ms, CONFIG_ZMK_MACRO_DEFAULT_TAP_MS), \ | ||
.count = DT_INST_PROP_LEN(n, bindings), \ | ||
.default_wait_ms = DT_PROP_OR(n, wait_ms, CONFIG_ZMK_MACRO_DEFAULT_WAIT_MS), \ | ||
.default_tap_ms = DT_PROP_OR(n, tap_ms, CONFIG_ZMK_MACRO_DEFAULT_TAP_MS), \ | ||
.count = DT_PROP_LEN(n, bindings), \ | ||
.bindings = TRANSFORMED_BEHAVIORS(n)}; \ | ||
DEVICE_DT_INST_DEFINE(n, behavior_macro_init, NULL, &behavior_macro_state_##n, \ | ||
&behavior_macro_config_##n, APPLICATION, \ | ||
CONFIG_KERNEL_INIT_PRIORITY_DEFAULT, &behavior_macro_driver_api); | ||
|
||
DT_INST_FOREACH_STATUS_OKAY(MACRO_INST) | ||
DEVICE_DT_DEFINE(n, behavior_macro_init, NULL, &behavior_macro_state_##n, \ | ||
&behavior_macro_config_##n, APPLICATION, CONFIG_KERNEL_INIT_PRIORITY_DEFAULT, \ | ||
&behavior_macro_driver_api); |
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.
n
is now a node ID instead of an integer. Should it be renamed?
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.
Renamed.
docs/docs/behaviors/macros.md
Outdated
``` | ||
|
||
:::note | ||
The `MACRO_PLACEHOLDER` is defined as a convenient way to avoid just putting `0` in that location. It does _not_ control what parameters are substituted; only the macro controls do that. |
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.
Might be useful to first explain that you still need to put something in for every binding, even if it will just get overwritten due to ¯o_param_XtoY
. Otherwise, it might be confusing why you would need to put 0
there, and therefore why you might use this instead.
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, good call. I expanded on this and pulled it out of the note
aside it had been hiding in.
docs/docs/behaviors/macros.md
Outdated
### Parameters | ||
|
||
When creating a macro that takes parameter(s), there are control 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 behavior in the `bindings` list of the macro. |
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.
Might be useful to clarify that it applies to the next non-macro control behavior, so ¯o_param1to1 ¯o_param2to2 @macro_press &foo 0 0
is a valid thing that works.
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.
Tweaked the wording here.
8a0c9b5
to
d759650
Compare
docs/docs/behaviors/macros.md
Outdated
@@ -62,6 +78,30 @@ 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 control when the parameters passed to the macro are used |
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.
This sentence (specifically "there are control when the parameters [...]") seems to be broken, I couldn't tell the intention.
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.
This was... incomprehensible.... Remind me not to write docs late at night when I'm tired. Reworded, to hopefully actually make sense now. Thanks!
d759650
to
91f5721
Compare
a300849
to
2cfbe1b
Compare
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.
Docs LGTM, noted a couple things in the logging and about the tests.
app/tests/macros/place-holder-parameters/native_posix_64.keymap
Outdated
Show resolved
Hide resolved
2cfbe1b
to
80beaec
Compare
a0df8bf
to
de25544
Compare
* Add two new compatibles for macros that take one or two parameters when bound in a keymap. * Use `¯o_param_1to1`, `¯o_param_1to2`, `¯o_param_2to1`, and `¯o_param_2to2` control entries in the bindings for the macro to have the next binding entry have it's values substituted. Co-authored-by: Cem Aksoylar <caksoylar@users.noreply.github.com>
de25544
to
3130e41
Compare
Ok, draft for now until I can create updated docs, but hoping for some feedback on this idea. We use more marker bindings to trigger different behavior for the next binding entry, e.g.
¯o_param_1to1
.So a new macro that surround a given letter with quotes:
Could then be used in your keymap like
"e_letter A
.Thoughts?