zmk
zmk copied to clipboard
feat(behaviors): Swapper implementation
Implementation of #997. Smart interrupt is probably not the name we should use, but I was out of ideas, help wanted!
I have been testing the window swapper implementation as in the example for the last day and it is working well so far. It neatly fixes #997 using a combination of first and second approaches proposed in there.
About the "tri-state" naming: It might be confusable with the "tri-layer" (lower+raise = adjust
) pattern that we support with conditional layers.
About the "tri-state" naming: It might be confusable with the "tri-layer" (
lower+raise = adjust
) pattern that we support with conditional layers.
Maybe three-phase would be better, or would 'triplex' or 'treble' work here? Start/Continue/End Behavior gets the point across, but I feel like it's a bit too specific.
I like the "bookend"/"sandwich" suggestions from @petejohanson personally, since it is like the repeatable "continue" behavior is bookended by special start and end events. Three-phase also sounds good.
@petejohanson Apologies for the delay. I think the bookend name is much better. As for the behavior queue, my understanding was that the keymap_* functions would be used here since we want the action taken immediately?
@petejohanson Apologies for the delay. I think the bookend name is much better. As for the behavior queue, my understanding was that the keymap_* functions would be used here since we want the action taken immediately?
Until I figure out an effective way to push items at the front of the queue, we will need to use the keymap function approach.
I have been using this behavior for a while now and love the versatility of it. Two thoughts:
1/ I noted that when binding the behavior to a combo, then one must include the combo keys in ignore-key-positions
. Otherwise, the exit condition will be triggered immediately. No big deal, but if there's an easy fix that might be cleaner.
2/ There is quite a bit of overlap between this and caps-word (and its generalization in #1451), the main difference being that tri-state
is position-based whereas caps-word
is keycode-based. Would it make sense to merge the two approaches? Say, by adding a ignore-key-events
keyword to tri-state
. One could then re-implement caps-word
with something like
bindings = <&kt LSHIFT>, <&none>, <&kt LSHIFT>;
ignore-key-events = <A B C D E F ...>
(For convenience, it could make sense to also have ignore-alphas
, ignore-numbers
and ignore-mods
options that set ignore-key-events
accordingly).
This is fantastic. In QMK I had a user space function for this same functionality, but it was just for alt + tab Currently in ZMK I'm doing it with a dedicated layer and two macros. Having this general solution will be Great
Ive been playing with this behavior and it seems to work for simple use cases that relay on <&kp>.
I couldn't get it to work the way I wanted when mixing and using the <&sk> and <&sl> behaviors. Some examples
t3_mod3: swapper_mod3 {
label = "swapper_mod3";
compatible = "zmk,behavior-tri-state";
#binding-cells = <0>;
bindings = <&to charsA>, <&sk LA(LSHFT)>, <&kp N3>;
ignored-key-positions = <8 9 10 11 12 22 23 24 25 26 40 41 42 43 44 60 61 62 63 64>;
ignored-layers = <0 charsA_m charsA>;
};
t3_mod4: swapper_mod4 {
label = "swapper_mod4";
compatible = "zmk,behavior-tri-state";
#binding-cells = <0>;
//bindings = <&skq LALT>, <&sl charsA>, <&kp N4>; //this combination doesn't work;
bindings = <&none>, <&sl charsA>, <&sk LA(LSHFT)>;
// ignored-key-positions = <46 65>;
ignored-layers = <charsA>;
};
@danielo515 can you shed some light onto your magic alt-tab implementation? I looked into your config and I'm a bit lost :) would love just general explanation how you've managed to pull it off. Thanks!
@danielo515 can you shed some light onto your magic alt-tab implementation? I looked into your config and I'm a bit lost :) would love just general explanation how you've managed to pull it off. Thanks!
It is very simple in reality. I have a key that moves to app switch layer, which has all keys to make me leave that layer, except the ones I want to use (left, right, and Q for quit). Here is the layer: https://github.com/danielo515/glove80-zmk-config/blob/40d69811521b449946ec341afe3eadedeb2a846e/config/glove80.keymap#L460-L471
And the relevant macros are this ones: https://github.com/danielo515/glove80-zmk-config/blob/40d69811521b449946ec341afe3eadedeb2a846e/config/glove80.keymap#L156-L180
It is very simple in reality. I have a key that moves to app switch layer, which has all keys to make me leave that layer, except the ones I want to use (left, right, and Q for quit).
Ah, so it's not really a one key swapper like QMK, it's just a new layer with different keys that you use to switch between the windows, right?
You can make it a one-key swapper if you want. Just don't bind the arrows. The docs included with this PR feature an example.
@petejohanson Apologies for the delay. I think the bookend name is much better. As for the behavior queue, my understanding was that the keymap_* functions would be used here since we want the action taken immediately?
I say we go w/ bookend term, barring any major disagreement. Can you update this with naming, docs, etc that reflect that?
I'm trying to use &swap
on an encoder and can't seem to get it to fire although it compiles fine... any ideas?
I have this running on my Glove80 and it's working pretty well so far, but I'm running into an issue. I have the following definition:
macswap: macswap {
label = "macswap";
compatible = "zmk,behavior-tri-state";
#binding-cells = <0>;
bindings = <&kt LGUI>, <&kp TAB>, <&kt LGUI>;
ignored-key-positions = <38>; // shift
}
In my layout, I have a key with &sk LGUI
and a key with &macswap
next to each other. The problem I'm running into is if I accidentally hit &sk LGUI
before &macswap
, LGUI
is permanently sticky and I have to turn the keyboard off in order to reset it.
Thank you very much for all the effort. I gave it a try. Works when added directly to the keymap. When I add it via a combo though, it doesn't work. Does anybody have the same problem or a workaround?
Thank you very much for all the effort. I gave it a try. Works when added directly to the keymap. When I add it via a combo though, it doesn't work. Does anybody have the same problem or a workaround?
You have to include the combo locations in the ignore-positions. See https://github.com/zmkfirmware/zmk/pull/1366#issuecomment-1473051776
@urob thank you very much!!! I just noticed my mistake. For others that want to use it with zmk-nodefree-config, here's how it works.
ZMK_COMBO(sw_app, &swapper, 21 22 23, ALL, 100)
// Alt+Tab swapper, requires PR #1366
ZMK_BEHAVIOR(swapper, tri_state,
bindings = <&kt LGUI>, <&kp TAB>, <&kt LGUI>;
timeout-ms = <500>;
ignored-key-positions = <21 22 23>;
)