From 8337f595b4d4dcab7c708e2805ce962abb370a46 Mon Sep 17 00:00:00 2001 From: snoyer Date: Tue, 28 Nov 2023 20:25:54 +0400 Subject: [PATCH 1/3] add capslock behavior --- app/CMakeLists.txt | 1 + app/dts/behaviors.dtsi | 1 + app/dts/behaviors/capslock.dtsi | 84 +++++++ .../behaviors/zmk,behavior-capslock.yaml | 27 ++ app/src/behaviors/behavior_capslock.c | 233 ++++++++++++++++++ docs/docs/behaviors/capslock.md | 65 +++++ 6 files changed, 411 insertions(+) create mode 100644 app/dts/behaviors/capslock.dtsi create mode 100644 app/dts/bindings/behaviors/zmk,behavior-capslock.yaml create mode 100644 app/src/behaviors/behavior_capslock.c create mode 100644 docs/docs/behaviors/capslock.md diff --git a/app/CMakeLists.txt b/app/CMakeLists.txt index 41892915..017dfea7 100644 --- a/app/CMakeLists.txt +++ b/app/CMakeLists.txt @@ -69,6 +69,7 @@ if ((NOT CONFIG_ZMK_SPLIT) OR CONFIG_ZMK_SPLIT_ROLE_CENTRAL) target_sources(app PRIVATE src/events/modifiers_state_changed.c) target_sources(app PRIVATE src/events/keycode_state_changed.c) target_sources_ifdef(CONFIG_ZMK_HID_INDICATORS app PRIVATE src/hid_indicators.c) + target_sources(app PRIVATE src/behaviors/behavior_capslock.c) if (CONFIG_ZMK_BLE) target_sources(app PRIVATE src/events/ble_active_profile_changed.c) diff --git a/app/dts/behaviors.dtsi b/app/dts/behaviors.dtsi index 23f2fee2..0fd9c40c 100644 --- a/app/dts/behaviors.dtsi +++ b/app/dts/behaviors.dtsi @@ -20,3 +20,4 @@ #include #include #include +#include diff --git a/app/dts/behaviors/capslock.dtsi b/app/dts/behaviors/capslock.dtsi new file mode 100644 index 00000000..aab98a87 --- /dev/null +++ b/app/dts/behaviors/capslock.dtsi @@ -0,0 +1,84 @@ +/* + * Copyright (c) 2023 The ZMK Contributors + * + * SPDX-License-Identifier: MIT + */ + +#include + +/ { + behaviors { + /omit-if-no-ref/ capslock_on: behavior_capslock_on { + compatible = "zmk,behavior-capslock"; + label = "CAPSLOCK_ON"; + #binding-cells = <0>; + capslock-press-duration = <5>; + enable-on-press; + }; + + /omit-if-no-ref/ capslock_off: behavior_capslock_off { + compatible = "zmk,behavior-capslock"; + label = "CAPSLOCK_OFF"; + #binding-cells = <0>; + capslock-press-duration = <5>; + disable-on-release; + }; + + /omit-if-no-ref/ capslock_hold: behavior_capslock_hold { + compatible = "zmk,behavior-capslock"; + label = "CAPSLOCK_HOLD"; + #binding-cells = <0>; + capslock-press-duration = <5>; + enable-on-press; + disable-on-release; + }; + + /omit-if-no-ref/ capslock_word: behavior_capslock_word { + compatible = "zmk,behavior-capslock"; + label = "CAPSLOCK_WORD"; + #binding-cells = <0>; + capslock-press-duration = <5>; + enable-on-press; + disable-on-next-release; + disable-on-keys = ; + }; + + /* MacOS compatibility */ + + /omit-if-no-ref/ capslock_on_mac: behavior_capslock_on_mac { + compatible = "zmk,behavior-capslock"; + label = "CAPSLOCK_ON_MAC"; + #binding-cells = <0>; + capslock-press-duration = <95>; + enable-on-press; + }; + + /omit-if-no-ref/ capslock_off_mac: behavior_capslock_off_mac { + compatible = "zmk,behavior-capslock"; + label = "CAPSLOCK_OFF_MAC"; + #binding-cells = <0>; + capslock-press-duration = <95>; + disable-on-release; + }; + + /omit-if-no-ref/ capslock_hold_mac: behavior_capslock_hold_mac { + compatible = "zmk,behavior-capslock"; + label = "CAPSLOCK_HOLD_MAC"; + #binding-cells = <0>; + capslock-press-duration = <95>; + enable-on-press; + disable-on-release; + }; + + /omit-if-no-ref/ capslock_word_mac: behavior_capslock_word_mac { + compatible = "zmk,behavior-capslock"; + label = "CAPSLOCK_WORD_MAC"; + #binding-cells = <0>; + capslock-press-duration = <95>; + enable-on-press; + disable-on-next-release; + disable-on-keys = ; + }; + + }; +}; \ No newline at end of file diff --git a/app/dts/bindings/behaviors/zmk,behavior-capslock.yaml b/app/dts/bindings/behaviors/zmk,behavior-capslock.yaml new file mode 100644 index 00000000..17229559 --- /dev/null +++ b/app/dts/bindings/behaviors/zmk,behavior-capslock.yaml @@ -0,0 +1,27 @@ +# Copyright (c) 2023 The ZMK Contributors +# SPDX-License-Identifier: MIT + +description: Caps word behavior + +compatible: "zmk,behavior-capslock" + +include: zero_param.yaml + +properties: + enable-on-press: + type: boolean + disable-on-release: + type: boolean + disable-on-next-release: + type: boolean + disable-on-keys: + type: array + required: false + default: [] + capslock-press-keycode: + type: int + default: 0 # will default to `CAPSLOCK` + capslock-press-duration: + type: int + default: 95 # seems to be the shortest reliable delay on Mac + diff --git a/app/src/behaviors/behavior_capslock.c b/app/src/behaviors/behavior_capslock.c new file mode 100644 index 00000000..358a820c --- /dev/null +++ b/app/src/behaviors/behavior_capslock.c @@ -0,0 +1,233 @@ +/* + * Copyright (c) 2023 The ZMK Contributors + * + * SPDX-License-Identifier: MIT + */ + +#define DT_DRV_COMPAT zmk_behavior_capslock + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL); + +#define HID_INDICATORS_CAPSLOCK_BIT BIT(1) + +#if DT_HAS_COMPAT_STATUS_OKAY(DT_DRV_COMPAT) + +struct capslock_key_item { + uint16_t page; + uint32_t id; + uint8_t implicit_modifiers; +}; + +struct behavior_capslock_config { + uint8_t index; + uint32_t capslock_press_keycode; + uint32_t capslock_press_duration; + bool enable_on_press; + bool disable_on_release; + bool disable_on_next_release; + uint8_t disable_on_keys_count; + struct capslock_key_item disable_on_keys[]; +}; + +uint32_t config_capslock_press_keycode(const struct behavior_capslock_config *config) { + return config->capslock_press_keycode > 0 ? config->capslock_press_keycode : CAPSLOCK; +} + +struct behavior_capslock_data { + uint32_t position; + bool active; + bool just_activated; +}; + +static void toggle_capslock(const struct device *dev) { + const struct behavior_capslock_config *config = dev->config; + const struct behavior_capslock_data *data = dev->data; + + const int32_t keycode = config_capslock_press_keycode(config); + const struct zmk_behavior_binding kp_capslock = { + .behavior_dev = "KEY_PRESS", + .param1 = keycode, + }; + + LOG_DBG("queueing %dms capslock press (usage_page 0x%02X keycode 0x%02X)", + config->capslock_press_duration, ZMK_HID_USAGE_PAGE(keycode), + ZMK_HID_USAGE_ID(keycode)); + zmk_behavior_queue_add(data->position, kp_capslock, true, config->capslock_press_duration); + zmk_behavior_queue_add(data->position, kp_capslock, false, 0); +} + +static bool get_capslock_state(void) { + return zmk_hid_indicators_get_current_profile() & HID_INDICATORS_CAPSLOCK_BIT; +} + +static void set_capslock_state(const struct device *dev, const bool target_state) { + const bool current_state = get_capslock_state(); + + if (current_state != target_state) { + LOG_DBG("toggling capslock state from %d to %d", current_state, target_state); + toggle_capslock(dev); + } else { + LOG_DBG("capslock state is already %d", target_state); + } +} + +static void activate_capslock(const struct device *dev) { + struct behavior_capslock_data *data = dev->data; + + set_capslock_state(dev, true); + + data->just_activated |= !data->active; // gets reset in `on_capslock_binding_released` + data->active = true; +} + +static void deactivate_capslock(const struct device *dev) { + struct behavior_capslock_data *data = dev->data; + + set_capslock_state(dev, false); + data->active = false; +} + +static int on_capslock_binding_pressed(struct zmk_behavior_binding *binding, + struct zmk_behavior_binding_event event) { + const struct device *dev = device_get_binding(binding->behavior_dev); + const struct behavior_capslock_config *config = dev->config; + struct behavior_capslock_data *data = dev->data; + + data->position = event.position; + if (config->enable_on_press) { + LOG_DBG("activating capslock (enable-on-press)"); + activate_capslock(dev); + } + + return ZMK_BEHAVIOR_OPAQUE; +} + +static int on_capslock_binding_released(struct zmk_behavior_binding *binding, + struct zmk_behavior_binding_event event) { + const struct device *dev = device_get_binding(binding->behavior_dev); + const struct behavior_capslock_config *config = dev->config; + struct behavior_capslock_data *data = dev->data; + + if (config->disable_on_release) { + LOG_DBG("deactivating capslock (disable-on-release)"); + deactivate_capslock(dev); + } else if (config->disable_on_next_release && !data->just_activated) { + LOG_DBG("deactivating capslock (disable-on-next-release)"); + deactivate_capslock(dev); + } + + data->just_activated = false; + + return ZMK_BEHAVIOR_OPAQUE; +} + +static bool capslock_match_key_item(const struct capslock_key_item *key_items, + const size_t key_items_count, uint16_t usage_page, + uint8_t usage_id, uint8_t implicit_modifiers) { + for (int i = 0; i < key_items_count; ++i) { + const struct capslock_key_item *break_item = &key_items[i]; + LOG_DBG("checking disable-on-keys: usage_page 0x%02X keycode 0x%02X", break_item->page, + break_item->id); + if (break_item->page == usage_page && break_item->id == usage_id && + (break_item->implicit_modifiers & (implicit_modifiers | zmk_hid_get_explicit_mods())) == + break_item->implicit_modifiers) { + return true; + } + } + + return false; +} + +static const struct device *devs[DT_NUM_INST_STATUS_OKAY(DT_DRV_COMPAT)]; + +static int capslock_keycode_state_changed_listener(const zmk_event_t *eh) { + const struct zmk_keycode_state_changed *ev = as_zmk_keycode_state_changed(eh); + if (ev == NULL || !ev->state) { + return ZMK_EV_EVENT_BUBBLE; + } + + for (int i = 0; i < DT_NUM_INST_STATUS_OKAY(DT_DRV_COMPAT); i++) { + const struct device *dev = devs[i]; + if (dev == NULL) { + continue; + } + + struct behavior_capslock_data *data = dev->data; + if (!data->active) { + continue; + } + + const struct behavior_capslock_config *config = dev->config; + + if (ZMK_HID_USAGE(ev->usage_page, ev->keycode) == config_capslock_press_keycode(config)) { + if (get_capslock_state()) { + LOG_DBG("capslock being toggled off (capslock key event: usage_page 0x%02X keycode " + "0x%02X)", + ev->usage_page, ev->keycode); + data->active = false; + } + } else if (capslock_match_key_item(config->disable_on_keys, config->disable_on_keys_count, + ev->usage_page, ev->keycode, ev->implicit_modifiers)) { + LOG_DBG("deactivating capslock (disable-on-keys: usage_page 0x%02X keycode 0x%02X)", + ev->usage_page, ev->keycode); + deactivate_capslock(dev); + } + } + + return ZMK_EV_EVENT_BUBBLE; +} + +static const struct behavior_driver_api behavior_capslock_driver_api = { + .binding_pressed = on_capslock_binding_pressed, + .binding_released = on_capslock_binding_released, +}; + +ZMK_LISTENER(behavior_capslock, capslock_keycode_state_changed_listener); +ZMK_SUBSCRIPTION(behavior_capslock, zmk_keycode_state_changed); + +static int behavior_capslock_init(const struct device *dev) { + const struct behavior_capslock_config *config = dev->config; + devs[config->index] = dev; + return 0; +} + +#define PARSE_KEY_ITEM(i) \ + {.page = ZMK_HID_USAGE_PAGE(i), \ + .id = ZMK_HID_USAGE_ID(i), \ + .implicit_modifiers = SELECT_MODS(i)}, + +#define BREAK_ITEM(i, n) PARSE_KEY_ITEM(DT_INST_PROP_BY_IDX(n, disable_on_keys, i)) + +#define CAPSLOCK_INST(n) \ + static struct behavior_capslock_data behavior_capslock_data_##n = {.active = false}; \ + static struct behavior_capslock_config behavior_capslock_config_##n = { \ + .index = n, \ + .capslock_press_keycode = DT_INST_PROP(n, capslock_press_keycode), \ + .capslock_press_duration = DT_INST_PROP(n, capslock_press_duration), \ + .enable_on_press = DT_INST_PROP(n, enable_on_press), \ + .disable_on_release = DT_INST_PROP(n, disable_on_release), \ + .disable_on_next_release = DT_INST_PROP(n, disable_on_next_release), \ + .disable_on_keys = {UTIL_LISTIFY(DT_INST_PROP_LEN(n, disable_on_keys), BREAK_ITEM, n)}, \ + .disable_on_keys_count = DT_INST_PROP_LEN(n, disable_on_keys), \ + }; \ + DEVICE_DT_INST_DEFINE(n, behavior_capslock_init, NULL, &behavior_capslock_data_##n, \ + &behavior_capslock_config_##n, APPLICATION, \ + CONFIG_KERNEL_INIT_PRIORITY_DEFAULT, &behavior_capslock_driver_api); + +DT_INST_FOREACH_STATUS_OKAY(CAPSLOCK_INST) + +#endif diff --git a/docs/docs/behaviors/capslock.md b/docs/docs/behaviors/capslock.md new file mode 100644 index 00000000..533330e4 --- /dev/null +++ b/docs/docs/behaviors/capslock.md @@ -0,0 +1,65 @@ +--- +title: Capslock Behavior +sidebar_label: Capslock +--- + +## Summary + +The capslock behavior provides ways to create improved, configurable Caps Lock keys. +Pressing the regular Caps Lock key _toggles_ the state of caps lock on the host machine, making its effect dependent of said state. +This behavior can account for the current state to offer more control on how and when caps lock get activated and deactivated, regardless of the current status on the connected device. + +### Behavior Binding + +Four pre-configured instances are provided: + +- `&capslock_on` enables caps lock +- `&capslock_off` disables caps lock +- `&capslock_hold` enables caps lock while held, and disables it when released +- `&capslock_word` enables caps lock, and disables it when a word separator is typed or the key is pressed again + +In order to ensure compatibility with MacOS (which ignores short capslock presses by design), `&capslock_on_mac`, `&capslock_off_mac`, `&capslock_hold_mac`, `&capslock_word_mac` versions using a longer press duration of 95ms are also available. + +### Configuration + +The following options allow to customize the capslock behavior: + +- `enable-on-press`: enable caps lock when the key is pressed +- `disable-on-release`: disable caps lock when the key is released +- `disable-on-next-release`: disable caps lock when the key is released a second time +- `disable-on-keys`: list of keys after which to disable capslock +- `capslock-press-duration`: duration of the capslock key press to generate (in milliseconds) + +### Examples + +The pre-configured `capslock_word` (which enables caps lock when pressed, and disables it when `SPACE`, `TAB`, `ENTER`, or `ESC` is pressed, or the key is pressed again) is defined as follow: + +``` +capslock_word: behavior_capslock_word { + compatible = "zmk,behavior-capslock"; + label = "CAPSLOCK_WORD"; + #binding-cells = <0>; + capslock-press-ms = <5>; + enable-on-press; + disable-on-next-release; + disable-on-keys = ; +}; +``` + +A key to activate caps lock and disable it only after typing a whole line can be defined as follow: + +``` +/ { + capslock_line: capslock_line { + compatible = "zmk,behavior-capslock"; + label = "CAPSLOCK_LINE"; + #binding-cells = <0>; + enable-on-press; + disable-on-keys = ; + }; + + keymap { + ... + }; +}; +``` From 3be50897c749e4552e173108a53551e88365eb97 Mon Sep 17 00:00:00 2001 From: snoyer Date: Sat, 2 Dec 2023 20:14:14 +0400 Subject: [PATCH 2/3] add `enable-while-keys`, remove `&capslock_word` --- app/dts/behaviors/capslock.dtsi | 20 ------- .../behaviors/zmk,behavior-capslock.yaml | 4 ++ app/src/behaviors/behavior_capslock.c | 54 +++++++++++++------ docs/docs/behaviors/capslock.md | 32 +++++------ 4 files changed, 57 insertions(+), 53 deletions(-) diff --git a/app/dts/behaviors/capslock.dtsi b/app/dts/behaviors/capslock.dtsi index aab98a87..8692d48f 100644 --- a/app/dts/behaviors/capslock.dtsi +++ b/app/dts/behaviors/capslock.dtsi @@ -33,16 +33,6 @@ disable-on-release; }; - /omit-if-no-ref/ capslock_word: behavior_capslock_word { - compatible = "zmk,behavior-capslock"; - label = "CAPSLOCK_WORD"; - #binding-cells = <0>; - capslock-press-duration = <5>; - enable-on-press; - disable-on-next-release; - disable-on-keys = ; - }; - /* MacOS compatibility */ /omit-if-no-ref/ capslock_on_mac: behavior_capslock_on_mac { @@ -70,15 +60,5 @@ disable-on-release; }; - /omit-if-no-ref/ capslock_word_mac: behavior_capslock_word_mac { - compatible = "zmk,behavior-capslock"; - label = "CAPSLOCK_WORD_MAC"; - #binding-cells = <0>; - capslock-press-duration = <95>; - enable-on-press; - disable-on-next-release; - disable-on-keys = ; - }; - }; }; \ No newline at end of file diff --git a/app/dts/bindings/behaviors/zmk,behavior-capslock.yaml b/app/dts/bindings/behaviors/zmk,behavior-capslock.yaml index 17229559..beb4655d 100644 --- a/app/dts/bindings/behaviors/zmk,behavior-capslock.yaml +++ b/app/dts/bindings/behaviors/zmk,behavior-capslock.yaml @@ -18,6 +18,10 @@ properties: type: array required: false default: [] + enable-while-keys: + type: array + required: false + default: [] capslock-press-keycode: type: int default: 0 # will default to `CAPSLOCK` diff --git a/app/src/behaviors/behavior_capslock.c b/app/src/behaviors/behavior_capslock.c index 358a820c..0cfc6778 100644 --- a/app/src/behaviors/behavior_capslock.c +++ b/app/src/behaviors/behavior_capslock.c @@ -31,6 +31,10 @@ struct capslock_key_item { uint32_t id; uint8_t implicit_modifiers; }; +struct capslock_key_list { + const struct capslock_key_item *keys; + size_t len; +}; struct behavior_capslock_config { uint8_t index; @@ -39,8 +43,8 @@ struct behavior_capslock_config { bool enable_on_press; bool disable_on_release; bool disable_on_next_release; - uint8_t disable_on_keys_count; - struct capslock_key_item disable_on_keys[]; + struct capslock_key_list disable_on_keys; + struct capslock_key_list enable_while_keys; }; uint32_t config_capslock_press_keycode(const struct behavior_capslock_config *config) { @@ -135,12 +139,11 @@ static int on_capslock_binding_released(struct zmk_behavior_binding *binding, return ZMK_BEHAVIOR_OPAQUE; } -static bool capslock_match_key_item(const struct capslock_key_item *key_items, - const size_t key_items_count, uint16_t usage_page, +static bool capslock_match_key_item(const struct capslock_key_list list, uint16_t usage_page, uint8_t usage_id, uint8_t implicit_modifiers) { - for (int i = 0; i < key_items_count; ++i) { - const struct capslock_key_item *break_item = &key_items[i]; - LOG_DBG("checking disable-on-keys: usage_page 0x%02X keycode 0x%02X", break_item->page, + for (int i = 0; i < list.len; ++i) { + const struct capslock_key_item *break_item = &list.keys[i]; + LOG_DBG("checking key_list item: usage_page 0x%02X keycode 0x%02X", break_item->page, break_item->id); if (break_item->page == usage_page && break_item->id == usage_id && (break_item->implicit_modifiers & (implicit_modifiers | zmk_hid_get_explicit_mods())) == @@ -180,11 +183,22 @@ static int capslock_keycode_state_changed_listener(const zmk_event_t *eh) { ev->usage_page, ev->keycode); data->active = false; } - } else if (capslock_match_key_item(config->disable_on_keys, config->disable_on_keys_count, - ev->usage_page, ev->keycode, ev->implicit_modifiers)) { - LOG_DBG("deactivating capslock (disable-on-keys: usage_page 0x%02X keycode 0x%02X)", - ev->usage_page, ev->keycode); - deactivate_capslock(dev); + } else if (config->disable_on_keys.len > 0) { + LOG_DBG("checking %d disable-on-keys for usage_page 0x%02X keycode 0x%02X", + config->disable_on_keys.len, ev->usage_page, ev->keycode); + if (capslock_match_key_item(config->disable_on_keys, ev->usage_page, ev->keycode, + ev->implicit_modifiers)) { + LOG_DBG("deactivating capslock (disable-on-keys)"); + deactivate_capslock(dev); + } + } else if (config->enable_while_keys.len > 0) { + LOG_DBG("checking %d enable-while-keys for usage_page 0x%02X keycode 0x%02X", + config->enable_while_keys.len, ev->usage_page, ev->keycode); + if (!capslock_match_key_item(config->enable_while_keys, ev->usage_page, ev->keycode, + ev->implicit_modifiers)) { + LOG_DBG("deactivating capslock (enable-while-keys)"); + deactivate_capslock(dev); + } } } @@ -210,9 +224,19 @@ static int behavior_capslock_init(const struct device *dev) { .id = ZMK_HID_USAGE_ID(i), \ .implicit_modifiers = SELECT_MODS(i)}, -#define BREAK_ITEM(i, n) PARSE_KEY_ITEM(DT_INST_PROP_BY_IDX(n, disable_on_keys, i)) +#define KEY_ARRAY_ITEM(i, n, prop) PARSE_KEY_ITEM(DT_INST_PROP_BY_IDX(n, prop, i)) + +#define KEY_LIST_PROP(n, prop) \ + COND_CODE_1(DT_NODE_HAS_PROP(DT_DRV_INST(n), prop), \ + ({LISTIFY(DT_INST_PROP_LEN(n, prop), KEY_ARRAY_ITEM, (), n, prop)}), ({})) + +#define KEY_LIST(array) ((struct capslock_key_list){.keys = array, .len = ARRAY_SIZE(array)}) #define CAPSLOCK_INST(n) \ + static const struct capslock_key_item capslock_disable_on_keys_list_##n[] = \ + KEY_LIST_PROP(n, disable_on_keys); \ + static const struct capslock_key_item capslock_enable_while_keys_list_##n[] = \ + KEY_LIST_PROP(n, enable_while_keys); \ static struct behavior_capslock_data behavior_capslock_data_##n = {.active = false}; \ static struct behavior_capslock_config behavior_capslock_config_##n = { \ .index = n, \ @@ -221,8 +245,8 @@ static int behavior_capslock_init(const struct device *dev) { .enable_on_press = DT_INST_PROP(n, enable_on_press), \ .disable_on_release = DT_INST_PROP(n, disable_on_release), \ .disable_on_next_release = DT_INST_PROP(n, disable_on_next_release), \ - .disable_on_keys = {UTIL_LISTIFY(DT_INST_PROP_LEN(n, disable_on_keys), BREAK_ITEM, n)}, \ - .disable_on_keys_count = DT_INST_PROP_LEN(n, disable_on_keys), \ + .disable_on_keys = KEY_LIST(capslock_disable_on_keys_list_##n), \ + .enable_while_keys = KEY_LIST(capslock_enable_while_keys_list_##n), \ }; \ DEVICE_DT_INST_DEFINE(n, behavior_capslock_init, NULL, &behavior_capslock_data_##n, \ &behavior_capslock_config_##n, APPLICATION, \ diff --git a/docs/docs/behaviors/capslock.md b/docs/docs/behaviors/capslock.md index 533330e4..4297bcf8 100644 --- a/docs/docs/behaviors/capslock.md +++ b/docs/docs/behaviors/capslock.md @@ -11,14 +11,13 @@ This behavior can account for the current state to offer more control on how and ### Behavior Binding -Four pre-configured instances are provided: +Three pre-configured instances are provided: - `&capslock_on` enables caps lock - `&capslock_off` disables caps lock - `&capslock_hold` enables caps lock while held, and disables it when released -- `&capslock_word` enables caps lock, and disables it when a word separator is typed or the key is pressed again -In order to ensure compatibility with MacOS (which ignores short capslock presses by design), `&capslock_on_mac`, `&capslock_off_mac`, `&capslock_hold_mac`, `&capslock_word_mac` versions using a longer press duration of 95ms are also available. +In order to ensure compatibility with MacOS (which ignores short capslock presses by design), `&capslock_on_mac`, `&capslock_off_mac`, `&capslock_hold_mac` versions using a longer press duration of 95ms are also available. ### Configuration @@ -28,11 +27,12 @@ The following options allow to customize the capslock behavior: - `disable-on-release`: disable caps lock when the key is released - `disable-on-next-release`: disable caps lock when the key is released a second time - `disable-on-keys`: list of keys after which to disable capslock +- `enable-while-keys`: list of keys for which to keep capslock enabled - `capslock-press-duration`: duration of the capslock key press to generate (in milliseconds) ### Examples -The pre-configured `capslock_word` (which enables caps lock when pressed, and disables it when `SPACE`, `TAB`, `ENTER`, or `ESC` is pressed, or the key is pressed again) is defined as follow: +In order to create a `capslock_word` key which enables caps lock when pressed, and disables it when `SPACE`, `TAB`, `ENTER`, or `ESC` is pressed, or the key is pressed again, a custom instance can be defined as follows: ``` capslock_word: behavior_capslock_word { @@ -46,20 +46,16 @@ capslock_word: behavior_capslock_word { }; ``` -A key to activate caps lock and disable it only after typing a whole line can be defined as follow: +In order to define a similar key using a list keys for which capslock should be kept enabled, akin to the `&caps_word`'s `continue-list`, the `enable-while-keys` proerty can be used instead of `disable-on-keys` (note that, unlike `&caps_word`, this behavior does not use any implicit whitelist so an exhaustive contination list must be provided): ``` -/ { - capslock_line: capslock_line { - compatible = "zmk,behavior-capslock"; - label = "CAPSLOCK_LINE"; - #binding-cells = <0>; - enable-on-press; - disable-on-keys = ; - }; - - keymap { - ... - }; +capslock_word: behavior_capslock_word { + compatible = "zmk,behavior-capslock"; + label = "CAPSLOCK_WORD"; + #binding-cells = <0>; + capslock-press-ms = <5>; + enable-on-press; + disable-on-next-release; + enable-while-keys = ; }; -``` +``` \ No newline at end of file From f268b313a8ee7c0a14786691029d0d701d48f801 Mon Sep 17 00:00:00 2001 From: snoyer Date: Sat, 2 Dec 2023 20:43:21 +0400 Subject: [PATCH 3/3] format --- app/dts/behaviors/capslock.dtsi | 110 +++++++++--------- .../behaviors/zmk,behavior-capslock.yaml | 1 - docs/docs/behaviors/capslock.md | 2 +- 3 files changed, 56 insertions(+), 57 deletions(-) diff --git a/app/dts/behaviors/capslock.dtsi b/app/dts/behaviors/capslock.dtsi index 8692d48f..8aecbad4 100644 --- a/app/dts/behaviors/capslock.dtsi +++ b/app/dts/behaviors/capslock.dtsi @@ -7,58 +7,58 @@ #include / { - behaviors { - /omit-if-no-ref/ capslock_on: behavior_capslock_on { - compatible = "zmk,behavior-capslock"; - label = "CAPSLOCK_ON"; - #binding-cells = <0>; - capslock-press-duration = <5>; - enable-on-press; - }; - - /omit-if-no-ref/ capslock_off: behavior_capslock_off { - compatible = "zmk,behavior-capslock"; - label = "CAPSLOCK_OFF"; - #binding-cells = <0>; - capslock-press-duration = <5>; - disable-on-release; - }; - - /omit-if-no-ref/ capslock_hold: behavior_capslock_hold { - compatible = "zmk,behavior-capslock"; - label = "CAPSLOCK_HOLD"; - #binding-cells = <0>; - capslock-press-duration = <5>; - enable-on-press; - disable-on-release; - }; - - /* MacOS compatibility */ - - /omit-if-no-ref/ capslock_on_mac: behavior_capslock_on_mac { - compatible = "zmk,behavior-capslock"; - label = "CAPSLOCK_ON_MAC"; - #binding-cells = <0>; - capslock-press-duration = <95>; - enable-on-press; - }; - - /omit-if-no-ref/ capslock_off_mac: behavior_capslock_off_mac { - compatible = "zmk,behavior-capslock"; - label = "CAPSLOCK_OFF_MAC"; - #binding-cells = <0>; - capslock-press-duration = <95>; - disable-on-release; - }; - - /omit-if-no-ref/ capslock_hold_mac: behavior_capslock_hold_mac { - compatible = "zmk,behavior-capslock"; - label = "CAPSLOCK_HOLD_MAC"; - #binding-cells = <0>; - capslock-press-duration = <95>; - enable-on-press; - disable-on-release; - }; - - }; -}; \ No newline at end of file + behaviors { + /omit-if-no-ref/ capslock_on: behavior_capslock_on { + compatible = "zmk,behavior-capslock"; + label = "CAPSLOCK_ON"; + #binding-cells = <0>; + capslock-press-duration = <5>; + enable-on-press; + }; + + /omit-if-no-ref/ capslock_off: behavior_capslock_off { + compatible = "zmk,behavior-capslock"; + label = "CAPSLOCK_OFF"; + #binding-cells = <0>; + capslock-press-duration = <5>; + disable-on-release; + }; + + /omit-if-no-ref/ capslock_hold: behavior_capslock_hold { + compatible = "zmk,behavior-capslock"; + label = "CAPSLOCK_HOLD"; + #binding-cells = <0>; + capslock-press-duration = <5>; + enable-on-press; + disable-on-release; + }; + + /* MacOS compatibility */ + + /omit-if-no-ref/ capslock_on_mac: behavior_capslock_on_mac { + compatible = "zmk,behavior-capslock"; + label = "CAPSLOCK_ON_MAC"; + #binding-cells = <0>; + capslock-press-duration = <95>; + enable-on-press; + }; + + /omit-if-no-ref/ capslock_off_mac: behavior_capslock_off_mac { + compatible = "zmk,behavior-capslock"; + label = "CAPSLOCK_OFF_MAC"; + #binding-cells = <0>; + capslock-press-duration = <95>; + disable-on-release; + }; + + /omit-if-no-ref/ capslock_hold_mac: behavior_capslock_hold_mac { + compatible = "zmk,behavior-capslock"; + label = "CAPSLOCK_HOLD_MAC"; + #binding-cells = <0>; + capslock-press-duration = <95>; + enable-on-press; + disable-on-release; + }; + + }; +}; diff --git a/app/dts/bindings/behaviors/zmk,behavior-capslock.yaml b/app/dts/bindings/behaviors/zmk,behavior-capslock.yaml index beb4655d..73fc4a38 100644 --- a/app/dts/bindings/behaviors/zmk,behavior-capslock.yaml +++ b/app/dts/bindings/behaviors/zmk,behavior-capslock.yaml @@ -28,4 +28,3 @@ properties: capslock-press-duration: type: int default: 95 # seems to be the shortest reliable delay on Mac - diff --git a/docs/docs/behaviors/capslock.md b/docs/docs/behaviors/capslock.md index 4297bcf8..02fa0275 100644 --- a/docs/docs/behaviors/capslock.md +++ b/docs/docs/behaviors/capslock.md @@ -58,4 +58,4 @@ capslock_word: behavior_capslock_word { disable-on-next-release; enable-while-keys = ; }; -``` \ No newline at end of file +```