Skip to content

Commit

Permalink
feat: Added optional flags to toggling behaviors allowing them to be …
Browse files Browse the repository at this point in the history
…explicitly toggle on or toggle off.
  • Loading branch information
Nick-Munnich committed Oct 15, 2024
1 parent a72327b commit 4ac5b3c
Show file tree
Hide file tree
Showing 22 changed files with 293 additions and 17 deletions.
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: "on-off"
enum:
- "on"
- "off"
- "on-off"
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: "on-off"
enum:
- "on"
- "off"
- "on-off"
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,
ON_OFF,
};

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 ON_OFF:
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)
39 changes: 30 additions & 9 deletions app/src/behaviors/behavior_toggle_layer.c
Original file line number Diff line number Diff line change
Expand Up @@ -17,22 +17,40 @@ 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,
ON_OFF,
};

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 ON_OFF:
return zmk_keymap_layer_toggle(binding->param1);
default:
return -ENOTSUP;
};
}

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

#if IS_ENABLED(CONFIG_ZMK_BEHAVIOR_METADATA)

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
10 changes: 8 additions & 2 deletions app/tests/toggle-layer/early-key-release/native_posix_64.keymap
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
8 changes: 8 additions & 0 deletions app/tests/toggle-layer/toggle-mode-on/keycode_events.snapshot
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. The `"off"` version is analogous.
Loading

0 comments on commit 4ac5b3c

Please sign in to comment.