Skip to content
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: Added toggle-mode, allowing toggle-on and toggle-off #2555

Open
wants to merge 5 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions app/dts/bindings/behaviors/zmk,behavior-key-toggle.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,13 @@ description: Key toggle behavior
compatible: "zmk,behavior-key-toggle"

include: one_param.yaml

properties:
toggle-mode:
type: string
required: false
default: "flip"
enum:
- "on"
- "off"
- "flip"
10 changes: 10 additions & 0 deletions app/dts/bindings/behaviors/zmk,behavior-toggle-layer.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,13 @@ description: Toggle Layer
compatible: "zmk,behavior-toggle-layer"

include: one_param.yaml

properties:
toggle-mode:
type: string
required: false
default: "flip"
enum:
- "on"
- "off"
- "flip"
35 changes: 31 additions & 4 deletions app/src/behaviors/behavior_key_toggle.c
Original file line number Diff line number Diff line change
Expand Up @@ -17,18 +17,41 @@

LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL);

enum toggle_mode {
ON,
OFF,
FLIP,
};

struct behavior_key_toggle_config {
enum toggle_mode toggle_mode;
};

static int behavior_key_toggle_init(const struct device *dev) { return 0; }

static int on_keymap_binding_pressed(struct zmk_behavior_binding *binding,
struct zmk_behavior_binding_event event) {
LOG_DBG("position %d keycode 0x%02X", event.position, binding->param1);
bool pressed = zmk_hid_is_pressed(binding->param1);
return raise_zmk_keycode_state_changed_from_encoded(binding->param1, !pressed, event.timestamp);
const struct behavior_key_toggle_config *cfg =
zmk_behavior_get_binding(binding->behavior_dev)->config;
switch (cfg->toggle_mode) {
case ON:
return raise_zmk_keycode_state_changed_from_encoded(binding->param1, true, event.timestamp);
case OFF:
return raise_zmk_keycode_state_changed_from_encoded(binding->param1, false,
event.timestamp);
case FLIP:
bool pressed = zmk_hid_is_pressed(binding->param1);
return raise_zmk_keycode_state_changed_from_encoded(binding->param1, !pressed,
event.timestamp);
default:
return -ENOTSUP;
};
}

static int on_keymap_binding_released(struct zmk_behavior_binding *binding,
struct zmk_behavior_binding_event event) {
return 0;
return ZMK_BEHAVIOR_OPAQUE;
}

#if IS_ENABLED(CONFIG_ZMK_BEHAVIOR_METADATA)
Expand Down Expand Up @@ -61,7 +84,11 @@ static const struct behavior_driver_api behavior_key_toggle_driver_api = {
};

#define KT_INST(n) \
BEHAVIOR_DT_INST_DEFINE(n, behavior_key_toggle_init, NULL, NULL, NULL, POST_KERNEL, \
static const struct behavior_key_toggle_config behavior_key_toggle_config_##n = { \
.toggle_mode = DT_ENUM_IDX(DT_DRV_INST(n), toggle_mode), \
}; \
BEHAVIOR_DT_INST_DEFINE(n, behavior_key_toggle_init, NULL, NULL, \
&behavior_key_toggle_config_##n, POST_KERNEL, \
CONFIG_KERNEL_INIT_PRIORITY_DEFAULT, &behavior_key_toggle_driver_api);

DT_INST_FOREACH_STATUS_OKAY(KT_INST)
37 changes: 29 additions & 8 deletions app/src/behaviors/behavior_toggle_layer.c
Original file line number Diff line number Diff line change
Expand Up @@ -17,15 +17,33 @@ LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL);

#if DT_HAS_COMPAT_STATUS_OKAY(DT_DRV_COMPAT)

struct behavior_tog_config {};
struct behavior_tog_data {};
enum toggle_mode {
ON,
OFF,
FLIP,
};

struct behavior_tog_config {
enum toggle_mode toggle_mode;
};

static int behavior_tog_init(const struct device *dev) { return 0; };

static int tog_keymap_binding_pressed(struct zmk_behavior_binding *binding,
struct zmk_behavior_binding_event event) {
LOG_DBG("position %d layer %d", event.position, binding->param1);
return zmk_keymap_layer_toggle(binding->param1);

const struct behavior_tog_config *cfg = zmk_behavior_get_binding(binding->behavior_dev)->config;
switch (cfg->toggle_mode) {
case ON:
return zmk_keymap_layer_activate(binding->param1);
case OFF:
return zmk_keymap_layer_deactivate(binding->param1);
case FLIP:
return zmk_keymap_layer_toggle(binding->param1);
default:
return -ENOTSUP;
};
}

static int tog_keymap_binding_released(struct zmk_behavior_binding *binding,
Expand Down Expand Up @@ -63,11 +81,14 @@ static const struct behavior_driver_api behavior_tog_driver_api = {
#endif // IS_ENABLED(CONFIG_ZMK_BEHAVIOR_METADATA)
};

static const struct behavior_tog_config behavior_tog_config = {};

static struct behavior_tog_data behavior_tog_data;
#define KT_INST(n) \
static const struct behavior_tog_config behavior_tog_config_##n = { \
.toggle_mode = DT_ENUM_IDX(DT_DRV_INST(n), toggle_mode), \
}; \
BEHAVIOR_DT_INST_DEFINE(n, behavior_tog_init, NULL, NULL, &behavior_tog_config_##n, \
POST_KERNEL, CONFIG_KERNEL_INIT_PRIORITY_DEFAULT, \
&behavior_tog_driver_api);

BEHAVIOR_DT_INST_DEFINE(0, behavior_tog_init, NULL, &behavior_tog_data, &behavior_tog_config,
POST_KERNEL, CONFIG_KERNEL_INIT_PRIORITY_DEFAULT, &behavior_tog_driver_api);
DT_INST_FOREACH_STATUS_OKAY(KT_INST)

#endif /* DT_HAS_COMPAT_STATUS_OKAY(DT_DRV_COMPAT) */
1 change: 1 addition & 0 deletions app/tests/keytoggle/kt-off/events.patterns
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
s/.*hid_listener_keycode_//p
4 changes: 4 additions & 0 deletions app/tests/keytoggle/kt-off/keycode_events.snapshot
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
released: usage_page 0x07 keycode 0x05 implicit_mods 0x00 explicit_mods 0x00
pressed: usage_page 0x07 keycode 0x05 implicit_mods 0x00 explicit_mods 0x00
released: usage_page 0x07 keycode 0x05 implicit_mods 0x00 explicit_mods 0x00
released: usage_page 0x07 keycode 0x05 implicit_mods 0x00 explicit_mods 0x00
29 changes: 29 additions & 0 deletions app/tests/keytoggle/kt-off/native_posix_64.keymap
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
#include "../behavior_keymap.dtsi"

&kt {
toggle-mode = "off";
};

&kscan {
events = <
ZMK_MOCK_PRESS(0,0,10)
ZMK_MOCK_RELEASE(0,0,10)
ZMK_MOCK_PRESS(1,1,10)
ZMK_MOCK_PRESS(0,0,10)
ZMK_MOCK_RELEASE(1,1,10)
ZMK_MOCK_RELEASE(0,0,10)
>;
};

/ {
keymap {
compatible = "zmk,keymap";

default_layer {
bindings = <
&kt B &trans
&trans &kp B
>;
};
};
};
1 change: 1 addition & 0 deletions app/tests/keytoggle/kt-on/events.patterns
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
s/.*hid_listener_keycode_//p
6 changes: 6 additions & 0 deletions app/tests/keytoggle/kt-on/keycode_events.snapshot
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
pressed: usage_page 0x07 keycode 0x05 implicit_mods 0x00 explicit_mods 0x00
pressed: unregistering usage_page 0x07 keycode 0x05 since it was already pressed
pressed: usage_page 0x07 keycode 0x05 implicit_mods 0x00 explicit_mods 0x00
pressed: unregistering usage_page 0x07 keycode 0x05 since it was already pressed
pressed: usage_page 0x07 keycode 0x05 implicit_mods 0x00 explicit_mods 0x00
released: usage_page 0x07 keycode 0x05 implicit_mods 0x00 explicit_mods 0x00
30 changes: 30 additions & 0 deletions app/tests/keytoggle/kt-on/native_posix_64.keymap
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
#include "../behavior_keymap.dtsi"

&kt {
toggle-mode = "on";
};


&kscan {
events = <
ZMK_MOCK_PRESS(0,0,10)
ZMK_MOCK_RELEASE(0,0,10)
ZMK_MOCK_PRESS(1,1,10)
ZMK_MOCK_PRESS(0,0,10)
ZMK_MOCK_RELEASE(1,1,10)
ZMK_MOCK_RELEASE(0,0,10)
>;
};

/ {
keymap {
compatible = "zmk,keymap";

default_layer {
bindings = <
&kt B &trans
&trans &kp B
>;
};
};
};
2 changes: 1 addition & 1 deletion app/tests/toggle-layer/behavior_keymap.dtsi
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
default_layer {
bindings = <
&kp B &tog 1
&kp D &kp G>;
&kp D &to 1>;
};

lower_layer {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,12 @@
#include "../behavior_keymap.dtsi"

&kscan {
events = <ZMK_MOCK_PRESS(0,0,10) ZMK_MOCK_PRESS(0,1,10) ZMK_MOCK_RELEASE(0,0,10) ZMK_MOCK_RELEASE(0,1,10)
ZMK_MOCK_PRESS(0,0,10) ZMK_MOCK_RELEASE(0,0,10)>;
events = <
ZMK_MOCK_PRESS(0,0,10)
ZMK_MOCK_PRESS(0,1,10)
ZMK_MOCK_RELEASE(0,0,10)
ZMK_MOCK_RELEASE(0,1,10)
ZMK_MOCK_PRESS(0,0,10)
ZMK_MOCK_RELEASE(0,0,10)
>;
};
4 changes: 4 additions & 0 deletions app/tests/toggle-layer/normal/keycode_events.snapshot
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,7 @@ tog_pressed: position 1 layer 1
tog_released: position 1 layer 1
kp_pressed: usage_page 0x0C keycode 0xB5 implicit_mods 0x00 explicit_mods 0x00
kp_released: usage_page 0x0C keycode 0xB5 implicit_mods 0x00 explicit_mods 0x00
tog_pressed: position 1 layer 1
tog_released: position 1 layer 1
kp_pressed: usage_page 0x07 keycode 0x05 implicit_mods 0x00 explicit_mods 0x00
kp_released: usage_page 0x07 keycode 0x05 implicit_mods 0x00 explicit_mods 0x00
11 changes: 10 additions & 1 deletion app/tests/toggle-layer/normal/native_posix_64.keymap
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,14 @@
#include "../behavior_keymap.dtsi"

&kscan {
events = <ZMK_MOCK_PRESS(0,1,10) ZMK_MOCK_RELEASE(0,1,10) ZMK_MOCK_PRESS(0,0,10) ZMK_MOCK_RELEASE(0,0,10)>;
events = <
ZMK_MOCK_PRESS(0,1,10)
ZMK_MOCK_RELEASE(0,1,10)
ZMK_MOCK_PRESS(0,0,10)
ZMK_MOCK_RELEASE(0,0,10)
ZMK_MOCK_PRESS(0,1,10)
ZMK_MOCK_RELEASE(0,1,10)
ZMK_MOCK_PRESS(0,0,10)
ZMK_MOCK_RELEASE(0,0,10)
>;
};
4 changes: 4 additions & 0 deletions app/tests/toggle-layer/toggle-mode-off/events.patterns
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
s/.*hid_listener_keycode/kp/p
s/.*to_keymap_binding/to/p
s/.*layer_changed/layer_changed/p
s/.*tog_keymap_binding/tog/p
14 changes: 14 additions & 0 deletions app/tests/toggle-layer/toggle-mode-off/keycode_events.snapshot
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
tog_pressed: position 1 layer 1
tog_released: position 1 layer 1
kp_pressed: usage_page 0x07 keycode 0x05 implicit_mods 0x00 explicit_mods 0x00
kp_released: usage_page 0x07 keycode 0x05 implicit_mods 0x00 explicit_mods 0x00
to_pressed: position 3 layer 1
layer_changed: layer 1 state 1
to_released: position 3 layer 1
kp_pressed: usage_page 0x0C keycode 0xB5 implicit_mods 0x00 explicit_mods 0x00
kp_released: usage_page 0x0C keycode 0xB5 implicit_mods 0x00 explicit_mods 0x00
tog_pressed: position 1 layer 1
layer_changed: layer 1 state 0
tog_released: position 1 layer 1
kp_pressed: usage_page 0x07 keycode 0x05 implicit_mods 0x00 explicit_mods 0x00
kp_released: usage_page 0x07 keycode 0x05 implicit_mods 0x00 explicit_mods 0x00
25 changes: 25 additions & 0 deletions app/tests/toggle-layer/toggle-mode-off/native_posix_64.keymap
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
#include <dt-bindings/zmk/keys.h>
#include <behaviors.dtsi>
#include <dt-bindings/zmk/kscan_mock.h>
#include "../behavior_keymap.dtsi"

&tog {
toggle-mode = "off";
};

&kscan {
events = <
ZMK_MOCK_PRESS(0,1,10)
ZMK_MOCK_RELEASE(0,1,10)
ZMK_MOCK_PRESS(0,0,10)
ZMK_MOCK_RELEASE(0,0,10)
ZMK_MOCK_PRESS(1,1,10)
ZMK_MOCK_RELEASE(1,1,10)
ZMK_MOCK_PRESS(0,0,10)
ZMK_MOCK_RELEASE(0,0,10)
ZMK_MOCK_PRESS(0,1,10)
ZMK_MOCK_RELEASE(0,1,10)
ZMK_MOCK_PRESS(0,0,10)
ZMK_MOCK_RELEASE(0,0,10)
>;
};
2 changes: 2 additions & 0 deletions app/tests/toggle-layer/toggle-mode-on/events.patterns
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
s/.*hid_listener_keycode/kp/p
s/.*tog_keymap_binding/tog/p
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
tog_pressed: position 1 layer 1
tog_released: position 1 layer 1
kp_pressed: usage_page 0x0C keycode 0xB5 implicit_mods 0x00 explicit_mods 0x00
kp_released: usage_page 0x0C keycode 0xB5 implicit_mods 0x00 explicit_mods 0x00
tog_pressed: position 1 layer 1
tog_released: position 1 layer 1
kp_pressed: usage_page 0x0C keycode 0xB5 implicit_mods 0x00 explicit_mods 0x00
kp_released: usage_page 0x0C keycode 0xB5 implicit_mods 0x00 explicit_mods 0x00
21 changes: 21 additions & 0 deletions app/tests/toggle-layer/toggle-mode-on/native_posix_64.keymap
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
#include <dt-bindings/zmk/keys.h>
#include <behaviors.dtsi>
#include <dt-bindings/zmk/kscan_mock.h>
#include "../behavior_keymap.dtsi"

&tog {
toggle-mode = "on";
};

&kscan {
events = <
ZMK_MOCK_PRESS(0,1,10)
ZMK_MOCK_RELEASE(0,1,10)
ZMK_MOCK_PRESS(0,0,10)
ZMK_MOCK_RELEASE(0,0,10)
ZMK_MOCK_PRESS(0,1,10)
ZMK_MOCK_RELEASE(0,1,10)
ZMK_MOCK_PRESS(0,0,10)
ZMK_MOCK_RELEASE(0,0,10)
>;
};
22 changes: 22 additions & 0 deletions docs/docs/keymaps/behaviors/key-toggle.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,3 +24,25 @@ Example:

You can use any keycode that works for `&kp` as parameter to `&kt`, however, [modified keys](../modifiers.mdx#modifier-functions) such as `LA(A)` will be toggled based on the status of the base keycode (in this case `A`).
In other words, modifiers are ignored when determining whether or not the key is currently pressed.

### Configuration

#### Toggle mode

If you wish to ensure that a key is pressed or released, rather than merely toggling, then you can do so with the `toggle-mode` property.
Define a new behavior and assign `"on"` or `"off"` to `toggle-mode`:

```dts
/ {
behaviors {
kt_on: key_toggle_on_only {
compatible = "zmk,behavior-key-toggle";
#binding-cells = <1>;
display-name = "Key Toggle On";
toggle-mode = "on";
};
};
};
```

You can then use `&kt_on` in place of `&kt` whenever you wish to only toggle a key on, and not toggle it off. An `"off"` version of the behavior can be defined similarly.
Loading