diff --git a/app/CMakeLists.txt b/app/CMakeLists.txt index 0c17ce04..b96f8094 100644 --- a/app/CMakeLists.txt +++ b/app/CMakeLists.txt @@ -41,7 +41,7 @@ if ((NOT CONFIG_ZMK_SPLIT) OR CONFIG_ZMK_SPLIT_ROLE_CENTRAL) target_sources_ifdef(CONFIG_ZMK_BEHAVIOR_KEY_TOGGLE app PRIVATE src/behaviors/behavior_key_toggle.c) target_sources(app PRIVATE src/behaviors/behavior_hold_tap.c) target_sources(app PRIVATE src/behaviors/behavior_sticky_key.c) - target_sources(app PRIVATE src/behaviors/behavior_caps_word.c) + target_sources(app PRIVATE src/behaviors/behavior_capslock.c) target_sources(app PRIVATE src/behaviors/behavior_key_repeat.c) target_sources(app PRIVATE src/behaviors/behavior_macro.c) target_sources(app PRIVATE src/behaviors/behavior_momentary_layer.c) diff --git a/app/dts/behaviors.dtsi b/app/dts/behaviors.dtsi index b3502cbb..7a4bbbc5 100644 --- a/app/dts/behaviors.dtsi +++ b/app/dts/behaviors.dtsi @@ -15,7 +15,7 @@ #include #include #include -#include +#include #include #include #include \ No newline at end of file diff --git a/app/dts/behaviors/caps_word.dtsi b/app/dts/behaviors/caps_word.dtsi deleted file mode 100644 index 9f67f6cd..00000000 --- a/app/dts/behaviors/caps_word.dtsi +++ /dev/null @@ -1,20 +0,0 @@ -/* - * Copyright (c) 2021 The ZMK Contributors - * - * SPDX-License-Identifier: MIT - */ - -#include - -/ { - behaviors { - /omit-if-no-ref/ caps_word: behavior_caps_word { - compatible = "zmk,behavior-caps-word"; - label = "CAPS_WORD"; - #binding-cells = <0>; - bindings = <&kp CAPSLOCK>; - break-list = ; - }; - }; -}; - diff --git a/app/dts/behaviors/capslock.dtsi b/app/dts/behaviors/capslock.dtsi new file mode 100644 index 00000000..c0662a7b --- /dev/null +++ b/app/dts/behaviors/capslock.dtsi @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2021 The ZMK Contributors + * + * SPDX-License-Identifier: MIT + */ + +#include + +/ { + behaviors { + /omit-if-no-ref/ caps_on: behavior_caps_on { + compatible = "zmk,behavior-capslock"; + label = "CAPS_ON"; + #binding-cells = <0>; + bindings = <&kp CAPSLOCK>; + enable-on-press; + }; + + /omit-if-no-ref/ caps_off: behavior_caps_off { + compatible = "zmk,behavior-capslock"; + label = "CAPS_OFF"; + #binding-cells = <0>; + bindings = <&kp CAPSLOCK>; + disable-on-release; + }; + + /omit-if-no-ref/ caps_hold: behavior_caps_hold { + compatible = "zmk,behavior-capslock"; + label = "CAPS_HOLD"; + #binding-cells = <0>; + bindings = <&kp CAPSLOCK>; + enable-on-press; + disable-on-release; + }; + + /omit-if-no-ref/ caps_word: behavior_caps_word { + compatible = "zmk,behavior-capslock"; + label = "CAPS_WORD"; + #binding-cells = <0>; + bindings = <&kp CAPSLOCK>; + enable-on-press; + disable-on-second-press; + disable-on-keys = ; + }; + }; +}; + diff --git a/app/dts/bindings/behaviors/zmk,behavior-caps-word.yaml b/app/dts/bindings/behaviors/zmk,behavior-capslock.yaml similarity index 50% rename from app/dts/bindings/behaviors/zmk,behavior-caps-word.yaml rename to app/dts/bindings/behaviors/zmk,behavior-capslock.yaml index 67f57206..fa4a476f 100644 --- a/app/dts/bindings/behaviors/zmk,behavior-caps-word.yaml +++ b/app/dts/bindings/behaviors/zmk,behavior-capslock.yaml @@ -3,14 +3,21 @@ description: Caps word behavior -compatible: "zmk,behavior-caps-word" +compatible: "zmk,behavior-capslock" include: zero_param.yaml properties: - break-list: + enable-on-press: + type: boolean + disable-on-release: + type: boolean + disable-on-second-press: + type: boolean + disable-on-keys: type: array - required: true + required: false + default: [] bindings: type: phandle-array required: true diff --git a/app/src/behaviors/behavior_caps_word.c b/app/src/behaviors/behavior_capslock.c similarity index 51% rename from app/src/behaviors/behavior_caps_word.c rename to app/src/behaviors/behavior_capslock.c index 9b7d81ba..28300a96 100644 --- a/app/src/behaviors/behavior_caps_word.c +++ b/app/src/behaviors/behavior_capslock.c @@ -4,7 +4,7 @@ * SPDX-License-Identifier: MIT */ -#define DT_DRV_COMPAT zmk_behavior_caps_word +#define DT_DRV_COMPAT zmk_behavior_capslock #include #include @@ -24,27 +24,31 @@ LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL); #if DT_HAS_COMPAT_STATUS_OKAY(DT_DRV_COMPAT) -struct caps_word_break_item { +struct capslock_key_item { uint16_t page; uint32_t id; uint8_t implicit_modifiers; }; -struct behavior_caps_word_config { +struct behavior_capslock_config { uint8_t index; struct zmk_behavior_binding capslock_binding; - uint8_t break_items_count; - struct caps_word_break_item break_items[]; + bool enable_on_press; + bool disable_on_release; + bool disable_on_second_press; + uint8_t disable_on_keys_count; + struct capslock_key_item disable_on_keys[]; }; -struct behavior_caps_word_data { +struct behavior_capslock_data { uint32_t position; bool active; + bool just_activated; }; static void toggle_capslock(const struct device *dev) { - const struct behavior_caps_word_config *config = dev->config; - const struct behavior_caps_word_data *data = dev->data; + const struct behavior_capslock_config *config = dev->config; + const struct behavior_capslock_data *data = dev->data; zmk_behavior_queue_add(data->position, config->capslock_binding, true, 0); zmk_behavior_queue_add(data->position, config->capslock_binding, false, 0); @@ -55,72 +59,84 @@ static void set_capslock_state(const struct device *dev, const bool target_state zmk_led_indicators_get_current_flags() & ZMK_LED_INDICATORS_CAPSLOCK_BIT; 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 was already %d", target_state); + LOG_DBG("capslock state is already %d", target_state); } } -static void activate_caps_word(const struct device *dev) { - struct behavior_caps_word_data *data = dev->data; +static void activate_capslock(const struct device *dev) { + struct behavior_capslock_data *data = dev->data; set_capslock_state(dev, true); + + if (!data->active) { + data->just_activated = true; + } data->active = true; } -static void deactivate_caps_word(const struct device *dev) { - struct behavior_caps_word_data *data = dev->data; +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_caps_word_binding_pressed(struct zmk_behavior_binding *binding, - struct zmk_behavior_binding_event event) { +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); - struct behavior_caps_word_data *data = dev->data; + const struct behavior_capslock_config *config = dev->config; + struct behavior_capslock_data *data = dev->data; data->position = event.position; - - if (data->active) { - deactivate_caps_word(dev); - } else { - activate_caps_word(dev); + if (config->enable_on_press) { + activate_capslock(dev); } return ZMK_BEHAVIOR_OPAQUE; } -static int on_caps_word_binding_released(struct zmk_behavior_binding *binding, - struct zmk_behavior_binding_event event) { +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 || (config->disable_on_second_press && !data->just_activated)) { + deactivate_capslock(dev); + } + + data->just_activated = false; + return ZMK_BEHAVIOR_OPAQUE; } -static const struct behavior_driver_api behavior_caps_word_driver_api = { - .binding_pressed = on_caps_word_binding_pressed, - .binding_released = on_caps_word_binding_released, +static const struct behavior_driver_api behavior_capslock_driver_api = { + .binding_pressed = on_capslock_binding_pressed, + .binding_released = on_capslock_binding_released, }; -static int caps_word_keycode_state_changed_listener(const zmk_event_t *eh); +static int capslock_keycode_state_changed_listener(const zmk_event_t *eh); -ZMK_LISTENER(behavior_caps_word, caps_word_keycode_state_changed_listener); -ZMK_SUBSCRIPTION(behavior_caps_word, zmk_keycode_state_changed); +ZMK_LISTENER(behavior_capslock, capslock_keycode_state_changed_listener); +ZMK_SUBSCRIPTION(behavior_capslock, zmk_keycode_state_changed); static const struct device *devs[DT_NUM_INST_STATUS_OKAY(DT_DRV_COMPAT)]; -static bool caps_word_is_break_item(const struct behavior_caps_word_config *config, - uint16_t usage_page, uint8_t usage_id, - uint8_t implicit_modifiers) { - for (int i = 0; i < config->break_items_count; i++) { - const struct caps_word_break_item *break_item = &config->break_items[i]; +static bool capslock_find_key_item(const struct behavior_capslock_config *config, + uint16_t usage_page, uint8_t usage_id, + uint8_t implicit_modifiers) { + for (int i = 0; i < config->disable_on_keys_count; i++) { + const struct capslock_key_item *break_item = &config->disable_on_keys[i]; LOG_DBG("Comparing with 0x%02X - 0x%02X (with implicit mods: 0x%02X)", break_item->page, break_item->id, break_item->implicit_modifiers); 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) { - LOG_DBG("Stopping capsword, found included usage: 0x%02X - 0x%02X", usage_page, - usage_id); + LOG_DBG("found included usage: 0x%02X - 0x%02X", usage_page, usage_id); return true; } } @@ -128,7 +144,7 @@ static bool caps_word_is_break_item(const struct behavior_caps_word_config *conf return false; } -static int caps_word_keycode_state_changed_listener(const zmk_event_t *eh) { +static int capslock_keycode_state_changed_listener(const zmk_event_t *eh) { struct zmk_keycode_state_changed *ev = as_zmk_keycode_state_changed(eh); if (ev == NULL || !ev->state) { return ZMK_EV_EVENT_BUBBLE; @@ -140,24 +156,24 @@ static int caps_word_keycode_state_changed_listener(const zmk_event_t *eh) { continue; } - struct behavior_caps_word_data *data = dev->data; + struct behavior_capslock_data *data = dev->data; if (!data->active) { continue; } - const struct behavior_caps_word_config *config = dev->config; + const struct behavior_capslock_config *config = dev->config; - if (caps_word_is_break_item(config, ev->usage_page, ev->keycode, ev->implicit_modifiers)) { - LOG_DBG("Deactivating caps_word for 0x%02X - 0x%02X", ev->usage_page, ev->keycode); - deactivate_caps_word(dev); + if (capslock_find_key_item(config, ev->usage_page, ev->keycode, ev->implicit_modifiers)) { + LOG_DBG("Deactivating capslock for 0x%02X - 0x%02X", ev->usage_page, ev->keycode); + deactivate_capslock(dev); } } return ZMK_EV_EVENT_BUBBLE; } -static int behavior_caps_word_init(const struct device *dev) { - const struct behavior_caps_word_config *config = dev->config; +static int behavior_capslock_init(const struct device *dev) { + const struct behavior_capslock_config *config = dev->config; devs[config->index] = dev; return 0; } @@ -167,7 +183,7 @@ static int behavior_caps_word_init(const struct device *dev) { .id = ZMK_HID_USAGE_ID(i), \ .implicit_modifiers = SELECT_MODS(i)}, -#define BREAK_ITEM(i, n) PARSE_BREAK(DT_INST_PROP_BY_IDX(n, break_list, i)) +#define BREAK_ITEM(i, n) PARSE_BREAK(DT_INST_PROP_BY_IDX(n, disable_on_keys, i)) #define _TRANSFORM_ENTRY(idx, node) \ { \ @@ -179,16 +195,19 @@ static int behavior_caps_word_init(const struct device *dev) { } #define KP_INST(n) \ - static struct behavior_caps_word_data behavior_caps_word_data_##n = {.active = false}; \ - static struct behavior_caps_word_config behavior_caps_word_config_##n = { \ + static struct behavior_capslock_data behavior_capslock_data_##n = {.active = false}; \ + static struct behavior_capslock_config behavior_capslock_config_##n = { \ .index = n, \ .capslock_binding = _TRANSFORM_ENTRY(0, n), \ - .break_items = {UTIL_LISTIFY(DT_INST_PROP_LEN(n, break_list), BREAK_ITEM, n)}, \ - .break_items_count = DT_INST_PROP_LEN(n, break_list), \ + .enable_on_press = DT_INST_PROP(n, enable_on_press), \ + .disable_on_release = DT_INST_PROP(n, disable_on_release), \ + .disable_on_second_press = DT_INST_PROP(n, disable_on_second_press), \ + .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_caps_word_init, NULL, &behavior_caps_word_data_##n, \ - &behavior_caps_word_config_##n, APPLICATION, \ - CONFIG_KERNEL_INIT_PRIORITY_DEFAULT, &behavior_caps_word_driver_api); + 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(KP_INST) diff --git a/docs/docs/behaviors/caps-word.md b/docs/docs/behaviors/caps-word.md deleted file mode 100644 index c8e62d3c..00000000 --- a/docs/docs/behaviors/caps-word.md +++ /dev/null @@ -1,56 +0,0 @@ ---- -title: Caps Word Behavior -sidebar_label: Caps Word ---- - -## Summary - -The caps word behavior activates caps lock when pressed, but will automatically deactivate it when any key in the break list is pressed, or if the caps word key is pressed again. For smaller keyboards using [mod-taps](/docs/behaviors/mod-tap), this can help avoid repeated alternating holds when typing words in all caps. - -### Behavior Binding - -- Reference: `&caps_word` - -Example: - -``` -&caps_word -``` - -### Configuration - -#### Break List - -By default, the caps word will remain active until space (`SPACE`), tab (`TAB`), enter (`ENTER`), or escape (`ESCAPE`) is pressed. If you would like to override this, you can set a new array of keys in the `break-list` property in your keymap: - -``` -&caps_word { - break-list = ; -}; - -/ { - keymap { - ... - }; -}; -``` - - -### Multiple Caps Breaks - -If you want to use multiple caps breaks with different codes to break the caps, you can add additional caps words instances to use in your keymap: - -``` -/ { - prog_caps: behavior_prog_caps_word { - compatible = "zmk,behavior-caps-word"; - label = "PROG_CAPS"; - #binding-cells = <0>; - break-list = ; - }; - - keymap { - ... - }; -}; -``` diff --git a/docs/docs/behaviors/capslock.md b/docs/docs/behaviors/capslock.md new file mode 100644 index 00000000..e751b0c2 --- /dev/null +++ b/docs/docs/behaviors/capslock.md @@ -0,0 +1,104 @@ +--- +title: Caps Lock Behavior +sidebar_label: Caps Lock +--- + +## Summary + +This set of behaviors offers enhahced versions of the caps lock key. + + +## Caps On + +Enables caps lock when pressed, regarless of the current caps lock state. + +### Behavior Binding +- Reference: `&caps_on` + +Example: + +``` +&caps_on +``` + + +## Caps Off + +Disables caps lock when released, regarless of the current caps lock state. + +### Behavior Binding +- Reference: `&caps_off` + +Example: + +``` +&caps_off +``` + + +## Caps Hold + +Enables caps lock when pressed, and disables it when released. + +### Behavior Binding +- Reference: `&caps_hold` + +Example: + +``` +&caps_hold +``` + + +## Caps Word + +Enables caps lock when pressed, and deactivate it when any key in the break list is pressed, or if the caps word key is pressed again. + +### Behavior Binding +- Reference: `&caps_word` + +Example: + +``` +&caps_word +``` + +### Configuration + +#### Word Separators + +By default, the caps word will remain active until space (`SPACE`), tab (`TAB`), enter (`ENTER`), or escape (`ESCAPE`) is pressed. If you would like to override this, you can set a new array of keys in the `disable-on-keys` property in your keymap: + +``` +&caps_word { + disable-on-keys = ; +}; + +/ { + keymap { + ... + }; +}; +``` + +### Multiple Caps Words + +If you want to use multiple caps words with different word separators, you can add additional caps word instances to use in your keymap: + +``` +/ { + prog_caps: behavior_prog_caps_word { + compatible = "zmk,behavior-capslock"; + label = "CAPS_WORD"; + #binding-cells = <0>; + bindings = <&kp CAPSLOCK>; + enable-on-press; + disable-on-second-press; + disable-on-keys = ; + }; + + keymap { + ... + }; +}; +``` diff --git a/docs/sidebars.js b/docs/sidebars.js index 7b445a29..17a29f5a 100644 --- a/docs/sidebars.js +++ b/docs/sidebars.js @@ -32,7 +32,7 @@ module.exports = { "behaviors/sticky-key", "behaviors/sticky-layer", "behaviors/tap-dance", - "behaviors/caps-word", + "behaviors/capslock", "behaviors/key-repeat", "behaviors/reset", "behaviors/bluetooth",