feat: Added `toggle-mode`, allowing toggle-on and toggle-off (#2555)

feat: added toggle mode to key and layer toggles

docs: documented toggle mode changes
This commit is contained in:
Nicolas Munnich 2024-12-13 00:13:56 +00:00 committed by GitHub
parent 7013158a67
commit 4ef231f4bb
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
23 changed files with 346 additions and 16 deletions

View File

@ -6,3 +6,13 @@ description: Key toggle behavior
compatible: "zmk,behavior-key-toggle" compatible: "zmk,behavior-key-toggle"
include: one_param.yaml include: one_param.yaml
properties:
toggle-mode:
type: string
required: false
default: "flip"
enum:
- "on"
- "off"
- "flip"

View File

@ -6,3 +6,13 @@ description: Toggle Layer
compatible: "zmk,behavior-toggle-layer" compatible: "zmk,behavior-toggle-layer"
include: one_param.yaml include: one_param.yaml
properties:
toggle-mode:
type: string
required: false
default: "flip"
enum:
- "on"
- "off"
- "flip"

View File

@ -17,18 +17,41 @@
LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL); 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 behavior_key_toggle_init(const struct device *dev) { return 0; }
static int on_keymap_binding_pressed(struct zmk_behavior_binding *binding, static int on_keymap_binding_pressed(struct zmk_behavior_binding *binding,
struct zmk_behavior_binding_event event) { struct zmk_behavior_binding_event event) {
LOG_DBG("position %d keycode 0x%02X", event.position, binding->param1); LOG_DBG("position %d keycode 0x%02X", event.position, binding->param1);
bool pressed = zmk_hid_is_pressed(binding->param1); const struct behavior_key_toggle_config *cfg =
return raise_zmk_keycode_state_changed_from_encoded(binding->param1, !pressed, event.timestamp); 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, static int on_keymap_binding_released(struct zmk_behavior_binding *binding,
struct zmk_behavior_binding_event event) { struct zmk_behavior_binding_event event) {
return 0; return ZMK_BEHAVIOR_OPAQUE;
} }
#if IS_ENABLED(CONFIG_ZMK_BEHAVIOR_METADATA) #if IS_ENABLED(CONFIG_ZMK_BEHAVIOR_METADATA)
@ -61,7 +84,11 @@ static const struct behavior_driver_api behavior_key_toggle_driver_api = {
}; };
#define KT_INST(n) \ #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); CONFIG_KERNEL_INIT_PRIORITY_DEFAULT, &behavior_key_toggle_driver_api);
DT_INST_FOREACH_STATUS_OKAY(KT_INST) DT_INST_FOREACH_STATUS_OKAY(KT_INST)

View File

@ -17,15 +17,33 @@ LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL);
#if DT_HAS_COMPAT_STATUS_OKAY(DT_DRV_COMPAT) #if DT_HAS_COMPAT_STATUS_OKAY(DT_DRV_COMPAT)
struct behavior_tog_config {}; enum toggle_mode {
struct behavior_tog_data {}; 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 behavior_tog_init(const struct device *dev) { return 0; };
static int tog_keymap_binding_pressed(struct zmk_behavior_binding *binding, static int tog_keymap_binding_pressed(struct zmk_behavior_binding *binding,
struct zmk_behavior_binding_event event) { struct zmk_behavior_binding_event event) {
LOG_DBG("position %d layer %d", event.position, binding->param1); 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, static int tog_keymap_binding_released(struct zmk_behavior_binding *binding,
@ -63,11 +81,14 @@ static const struct behavior_driver_api behavior_tog_driver_api = {
#endif // IS_ENABLED(CONFIG_ZMK_BEHAVIOR_METADATA) #endif // IS_ENABLED(CONFIG_ZMK_BEHAVIOR_METADATA)
}; };
static const struct behavior_tog_config behavior_tog_config = {}; #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);
static struct behavior_tog_data behavior_tog_data; DT_INST_FOREACH_STATUS_OKAY(KT_INST)
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);
#endif /* DT_HAS_COMPAT_STATUS_OKAY(DT_DRV_COMPAT) */ #endif /* DT_HAS_COMPAT_STATUS_OKAY(DT_DRV_COMPAT) */

View File

@ -0,0 +1 @@
s/.*hid_listener_keycode_//p

View File

@ -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

View File

@ -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
>;
};
};
};

View File

@ -0,0 +1 @@
s/.*hid_listener_keycode_//p

View File

@ -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

View File

@ -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
>;
};
};
};

View File

@ -9,7 +9,7 @@
default_layer { default_layer {
bindings = < bindings = <
&kp B &tog 1 &kp B &tog 1
&kp D &kp G>; &kp D &to 1>;
}; };
lower_layer { lower_layer {

View File

@ -4,6 +4,12 @@
#include "../behavior_keymap.dtsi" #include "../behavior_keymap.dtsi"
&kscan { &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) events = <
ZMK_MOCK_PRESS(0,0,10) ZMK_MOCK_RELEASE(0,0,10)>; 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)
>;
}; };

View File

@ -2,3 +2,7 @@ tog_pressed: position 1 layer 1
tog_released: position 1 layer 1 tog_released: position 1 layer 1
kp_pressed: usage_page 0x0C keycode 0xB5 implicit_mods 0x00 explicit_mods 0x00 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 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

View File

@ -4,5 +4,14 @@
#include "../behavior_keymap.dtsi" #include "../behavior_keymap.dtsi"
&kscan { &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)
>;
}; };

View File

@ -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

View File

@ -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

View File

@ -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)
>;
};

View File

@ -0,0 +1,2 @@
s/.*hid_listener_keycode/kp/p
s/.*tog_keymap_binding/tog/p

View File

@ -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

View File

@ -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)
>;
};

View File

@ -17,6 +17,14 @@ See the [zmk/app/dts/behaviors/](https://github.com/zmkfirmware/zmk/tree/main/ap
| --------------------------------- | ---- | ------------------------------------------------------------------------------------ | ------- | | --------------------------------- | ---- | ------------------------------------------------------------------------------------ | ------- |
| `CONFIG_ZMK_BEHAVIORS_QUEUE_SIZE` | int | Maximum number of behaviors to allow queueing from a macro or other complex behavior | 64 | | `CONFIG_ZMK_BEHAVIORS_QUEUE_SIZE` | int | Maximum number of behaviors to allow queueing from a macro or other complex behavior | 64 |
### Devicetree
Definition file: [zmk/app/dts/bindings/behaviors/behavior-metadata.yaml](https://github.com/zmkfirmware/zmk/blob/main/app/dts/bindings/behaviors/behavior-metadata.yaml)
| Property | Type | Description | Default |
| -------------- | ------ | -------------------------------------------------------------------------------- | ------- |
| `display-name` | string | Name of the layer, for use with a display or [ZMK Studio](../features/studio.md) | |
## Caps Word ## Caps Word
Creates a custom behavior that behaves similar to a caps lock but deactivates when any key not in a continue list is pressed. Creates a custom behavior that behaves similar to a caps lock but deactivates when any key not in a continue list is pressed.
@ -123,6 +131,52 @@ You can use the following nodes to tweak the default behaviors:
| ------------- | ------------------------------------------------ | | ------------- | ------------------------------------------------ |
| `&key_repeat` | [Key repeat](../keymaps/behaviors/key-repeat.md) | | `&key_repeat` | [Key repeat](../keymaps/behaviors/key-repeat.md) |
## Key Toggle
Creates a custom behavior that toggles a key code on, off, or switches between the two states.
See the [key toggle behavior](../keymaps/behaviors/key-toggle.md) documentation for more details and examples.
### Devicetree
Definition file: [zmk/app/dts/bindings/behaviors/zmk,behavior-key-toggle.yaml](https://github.com/zmkfirmware/zmk/blob/main/app/dts/bindings/behaviors/zmk%2Cbehavior-key-toggle.yaml)
Applies to: `compatible = "zmk,behavior-key-toggle"`
| Property | Type | Description | Default |
| ---------------- | ---- | ------------------------------ | ------- |
| `#binding-cells` | int | Must be `<1>` | |
| `toggle-mode` | | One of `on`, `off`, and `flip` | `flip` |
You can use the following node to tweak the default behavior:
| Node | Behavior |
| ----- | ------------------------------------------------ |
| `&kt` | [Key toggle](../keymaps/behaviors/key-toggle.md) |
## Layer Toggle
Creates a custom behavior that toggles a layer on, off, or switches between the two states.
See the [layer toggle behavior](../keymaps/behaviors/layers.md#toggle-layer) documentation for more details and examples.
### Devicetree
Definition file: [zmk/app/dts/bindings/behaviors/zmk,behavior-layer-toggle.yaml](https://github.com/zmkfirmware/zmk/blob/main/app/dts/bindings/behaviors/zmk%2Cbehavior-layer-toggle.yaml)
Applies to: `compatible = "zmk,behavior-layer-toggle"`
| Property | Type | Description | Default |
| ---------------- | ---- | ------------------------------ | ------- |
| `#binding-cells` | int | Must be `<1>` | |
| `toggle-mode` | | One of `on`, `off`, and `flip` | `flip` |
You can use the following node to tweak the default behavior:
| Node | Behavior |
| ------ | ----------------------------------------------------------- |
| `&tog` | [Layer toggle](../keymaps/behaviors/layers.md#toggle-layer) |
## Macro ## Macro
Creates a custom behavior which triggers a sequence of other behaviors. Creates a custom behavior which triggers a sequence of other behaviors.

View File

@ -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`). 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. 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.

View File

@ -103,6 +103,28 @@ Example:
&tog 3 &tog 3
``` ```
### Configuration
#### Toggle mode
If you wish to ensure that a layer is toggled on or off specifically, rather than switching between the two states, then you can do so with the `toggle-mode` property.
Define a new behavior and assign `"on"` or `"off"` to `toggle-mode`:
```dts
/ {
behaviors {
tog_on: toggle_layer_on_only {
compatible = "zmk,behavior-toggle-layer";
#binding-cells = <1>;
display-name = "Toggle Layer On";
toggle-mode = "on";
};
};
};
```
You can then use `&tog_on` in place of `&tog` whenever you wish to only toggle a layer on, and not toggle it off. An `"off"` version of the behavior can be defined similarly.
## Conditional Layers ## Conditional Layers
The "conditional layers" feature enables a particular layer when all layers in a specified set are active. The "conditional layers" feature enables a particular layer when all layers in a specified set are active.