zmk icon indicating copy to clipboard operation
zmk copied to clipboard

feat(behaviors): Add dynamic macros

Open nickconway opened this issue 2 years ago • 23 comments

Adds the dynamic macro behavior, allowing users to create macros on the fly.

nickconway avatar Jun 20 '22 01:06 nickconway

I am testing this and I can say that on the BT60 (nrf52840) the feature crashes if more than 30 keystrokes are entered. Otherwise, it works as expected and shows great flexibility in recording even keystroke combinations. If there is a memory hard limit that can be determined per processor, or an overall safe limit, it might be worth adding that because when going over the limit there are various odd things that happen.

I would suggest that the documentation be amended to say explicitly that RECORD is pressed a second time to stop the recording. This may be obvious, but there will be people confused.

claviger-pc avatar Jun 25 '22 06:06 claviger-pc

If there is a memory hard limit that can be determined per processor, or an overall safe limit, it might be worth adding that because when going over the limit there are various odd things that happen.

I think I'm going to add a config option to set the limit. Do you think it should stay in the recording state but not actually record any more binds when it hits the limit?

nickconway avatar Jun 25 '22 15:06 nickconway

If there is a memory hard limit that can be determined per processor, or an overall safe limit, it might be worth adding that because when going over the limit there are various odd things that happen.

I think I'm going to add a config option to set the limit. Do you think it should stay in the recording state but not actually record any more binds when it hits the limit?

It is a bit confusing because there is no way of telling whether the recording mode is still engaged. It one hit the limit, then there could be a problem that trying to stop the record mode would only engage it again. Perhaps the best would be to leave it in the recording state so that pressing record again would finish recording instead of start recording again.

claviger-pc avatar Jun 25 '22 15:06 claviger-pc

Hi, I tried the feature and, although I like it, I would like to share some feedback:

  • I believe the recording should also capture the time between keystrokes and replay them as they were captured. I've found myself in situations where I recorded a macro, but it was played too fast or too slow.
  • Also, if I record some keys, and then spam 'play', at some point the replayed keystrokes get messed up. Ideally I think if I hit play while playback is already ongoing, it should be a no-op.
  • I think the keyboard should not send keystrokes to the host during recording. It will be a nice way to tell if a recording is ongoing. Maybe this can be made configurable (e.g. two different bindings for recording, or globally through a config option).

Additionally, although I am personally very new to ZMK in general, I'd love to help getting this merged. Let me know if there is something I can help with :)

rdnt avatar Aug 07 '22 22:08 rdnt

Hi, I tried the feature and, although I like it, I would like to share some feedback:

  • I believe the recording should also capture the time between keystrokes and replay them as they were captured. I've found myself in situations where I recorded a macro, but it was played too fast or too slow.
  • Also, if I record some keys, and then spam 'play', at some point the replayed keystrokes get messed up. Ideally I think if I hit play while playback is already ongoing, it should be a no-op.
  • I think the keyboard should not send keystrokes to the host during recording. It will be a nice way to tell if a recording is ongoing. Maybe this can be made configurable (e.g. two different bindings for recording, or globally through a config option).

Additionally, although I am personally very new to ZMK in general, I'd love to help getting this merged. Let me know if there is something I can help with :)

By default, the dynamic macro should play back at the speed it was recorded at. Make sure you DON'T have wait-ms set.

Good idea on setting play to no-op when recording. I'll add that here shortly.

I can probably add the no-output pretty easily, but not sending the keystrokes to the host would make it hard to tell if you're inputting correctly, no?

nickconway avatar Aug 07 '22 22:08 nickconway

Hi, I tried the feature and, although I like it, I would like to share some feedback:

  • I believe the recording should also capture the time between keystrokes and replay them as they were captured. I've found myself in situations where I recorded a macro, but it was played too fast or too slow.
  • Also, if I record some keys, and then spam 'play', at some point the replayed keystrokes get messed up. Ideally I think if I hit play while playback is already ongoing, it should be a no-op.
  • I think the keyboard should not send keystrokes to the host during recording. It will be a nice way to tell if a recording is ongoing. Maybe this can be made configurable (e.g. two different bindings for recording, or globally through a config option).

Additionally, although I am personally very new to ZMK in general, I'd love to help getting this merged. Let me know if there is something I can help with :)

By default, the dynamic macro should play back at the speed it was recorded at. Make sure you DON'T have wait-ms set.

Good idea on setting play to no-op when recording. I'll add that here shortly.

I can probably add the no-output pretty easily, but not sending the keystrokes to the host would make it hard to tell if you're inputting correctly, no?

I've added all these now, to have no output set no-output in the definition for the dynamic macro!

nickconway avatar Aug 07 '22 23:08 nickconway

@nickconway

dynamic-macros.md: a macro will play back at the speed it was recorded

Wow I totally missed that part on the description 😃

Regarding no-output, in retrospect I believe you are right. I am glad you added the option to configure it though! Could be useful for others.

rdnt avatar Aug 08 '22 05:08 rdnt

@nickconway some additional notes:

  • The no-op suggestion was about hitting play while the playback of a macro is ongoing (not the recording). The one implemented indeed seems like a welcome bug-fix, but what is your view on this one?
  • I also think there is a bug somewhere... I tried recordingtest123 and during playback it flipped the output to test312 or similar. I re-flashed and I cannot reproduce it now. Will let you know if I can manage to properly reproduce this, just wanted to bring it to your attention.

*edited to add details about a possible bug

rdnt avatar Aug 08 '22 06:08 rdnt

  • The no-op suggestion was about hitting play while the playback of a macro is ongoing (not the recording). The one implemented indeed seems like a welcome bug-fix, but what is your view on this one?

So queuing up the macro to play back while it's already playing will probably have some weird effects. This happens with regular macros too. It's possible that we could keep track of the total runtime and not let it play again within that time, but since this is an issue with all macros, we should try to fix the root problem first if needed, which probably isn't trivial.

  • I also think there is a bug somewhere... I tried recordingtest123 and during playback it flipped the output to test312 or similar. I re-flashed and I cannot reproduce it now. Will let you know if I can manage to properly reproduce this, just wanted to bring it to your attention.

If you can reproduce it and get a log file that'd be great.

nickconway avatar Aug 08 '22 06:08 nickconway

How can I add this for my keyboard build?

idesignstuff avatar Feb 08 '23 01:02 idesignstuff

@idesignstuff hi, a good resource on how to configure your CI builds to use the branch that belongs to this PR is the following docs page: Beta Testing | ZMK Firmware.

You can also take a look at my config here for a complete implementation.

rdnt avatar Feb 08 '23 13:02 rdnt

@nickconway still new to ZMK so I may be wrong, but it looks like (as this is a macro) play and record can't be on the same key? I'd like to set it so hold records and tapping plays using tap-dance or hold-tap but it doesn't look like it's possible?

ggstrader avatar Mar 21 '23 07:03 ggstrader

Is there any possibility that &key_repeat can repeat the last played macro? I suppose this applies to any macro, not just dynamic ones. My usecase is that I have PLAY/RECORD nested in a util layer, but if I want to easily execute the macro many times, I can play the macro but then spam repeat key on my base layer (which spams PLAY).

theol0403 avatar Apr 12 '23 23:04 theol0403

Does not seem to be working when merged with the latest updates to Zephyr 3.5. It builds but throws these messages:

  212 | static const struct behavior_driver_api behavior_macro_driver_api = {
      |                                         ^~~~~~~~~~~~~~~~~~~~~~~~~
/home/pc/zmk/app/src/behaviors/behavior_macro.c:110:12: warning: 'behavior_macro_init' defined but not used [-Wunused-function]
  110 | static int behavior_macro_init(const struct device *dev) {
      |            ^~~~~~~~~~~~~~~~~~~
[33/310] Building C object CMakeFiles/app.dir/src/behaviors/behavior_dynamic_macro.c.obj
/home/pc/zmk/app/src/behaviors/behavior_dynamic_macro.c:218:13: warning: APPLICATION device driver level is deprecated
  218 | DT_INST_FOREACH_STATUS_OKAY(DYNAMIC_MACRO_INST)

Any ideas? It still worked on zephyr 3.2

claviger-pc avatar Feb 15 '24 17:02 claviger-pc

After the recent changes, I still cannot make it work with the latest zmk branch (3.5), although if I change APPLICATION in line 218 of the behaviors/behavior_dynamic_macro.c to POST_KERNEL, no errors are thrown.

I am building my BT60 repo against my zmk repo locally: https://github.com/claviger-pc/zmk https://github.com/claviger-pc/bt60_v1_clav

claviger-pc avatar Apr 16 '24 21:04 claviger-pc

After the recent changes, I still cannot make it work with the latest zmk branch (3.5), although if I change APPLICATION in line 218 of the behaviors/behavior_dynamic_macro.c to POST_KERNEL, no errors are thrown.

I am building my BT60 repo against my zmk repo locally: https://github.com/claviger-pc/zmk https://github.com/claviger-pc/bt60_v1_clav

Should be fixed now!

nickconway avatar Apr 16 '24 22:04 nickconway

After the recent changes, I still cannot make it work with the latest zmk branch (3.5), although if I change APPLICATION in line 218 of the behaviors/behavior_dynamic_macro.c to POST_KERNEL, no errors are thrown. I am building my BT60 repo against my zmk repo locally: https://github.com/claviger-pc/zmk https://github.com/claviger-pc/bt60_v1_clav

Should be fixed now!

I am sorry that I did not make my problem clearer. Changing APPLICATION to POST_KERNEL did stop the error when building, but it did not fix anything when trying to build with the Zephyr 3.5 updates. In fact, when I was trying to build before, the mapped places on the keymap for RECORD and PLAY were transparent rather than non-functional (I have them on a secondary layer). Now, however, I made sure I am building according to zmk master and your dynamic-macro commits. It build with no errors, and the keys are not longer transparent, but seemed to do nothing. Then I decided to see what logging would tell me, and I find that in fact the recording and playback do seem to work, but there is no output, that is, the actual keypresses are not registered (MacOS).

[00:07:55.233,520] <dbg> zmk: on_dynamic_macro_binding_pressed: Recording Status: 1
[00:07:55.233,520] <dbg> zmk: on_dynamic_macro_binding_pressed: Recording new macro: 15

[00:07:56.952,545] <dbg> zmk: on_dynamic_macro_binding_pressed: Recording Status: 0
[00:07:57.216,247] <dbg> zmk: kscan_matrix_read: Sending event at 1,1 state off

[00:07:57.963,531] <dbg> zmk: on_dynamic_macro_binding_pressed: Playing Dynamic Macro
[00:07:57.963,531] <dbg> zmk: queue_dynamic_macro: Iterating dynamic macro bindings - count: 8
[00:07:57.963,592] <dbg> zmk: behavior_queue_process_next: Invoking KEY_PRESS: 0x70004 0x00
[00:07:57.963,623] <dbg> zmk: behavior_queue_process_next: Processing next queued behavior in 48ms
[00:07:58.011,749] <dbg> zmk: behavior_queue_process_next: Invoking KEY_PRESS: 0x70016 0x00
[00:07:58.011,779] <dbg> zmk: behavior_queue_process_next: Processing next queued behavior in 44ms
[00:07:58.055,908] <dbg> zmk: behavior_queue_process_next: Invoking KEY_PRESS: 0x70007 0x00
[00:07:58.055,969] <dbg> zmk: behavior_queue_process_next: Processing next queued behavior in 94ms

claviger-pc avatar Apr 17 '24 01:04 claviger-pc

@claviger-pc How close is this feature to being complete? It's one of the key feature differences (in my opinion) between ZMK and QMK, and I'd love to implement it into my project.

chatter-b0x avatar May 02 '24 00:05 chatter-b0x

@claviger-pc How close is this feature to being complete? It's one of the key feature differences (in my opinion) between ZMK and QMK, and I'd love to implement it into my project.

This feature is also available in QMK: https://github.com/qmk/qmk_firmware/blob/master/docs/feature_dynamic_macros.md

Unfortunately this PR does not seem to work on the latest version of ZMK, see my comments above.

claviger-pc avatar May 02 '24 01:05 claviger-pc

@claviger-pc How close is this feature to being complete? It's one of the key feature differences (in my opinion) between ZMK and QMK, and I'd love to implement it into my project.

This feature is also available in QMK: https://github.com/qmk/qmk_firmware/blob/master/docs/feature_dynamic_macros.md

Unfortunately this PR does not seem to work on the latest version of ZMK, see my comments above.

That is unfortunate, hopefully this can be resolved in the coming month or so.

chatter-b0x avatar May 02 '24 01:05 chatter-b0x

@nickconway -- I tried again today to build against your branch and the same thing happens: there is no output. Is this working for you? The only thing I can find in my setup that is non standard is that I had defined wait-ms as 10ms. No errors in building, and the logs show as above, but there is no output. I also tried it without the wait-ms and it did the same

claviger-pc avatar May 08 '24 01:05 claviger-pc