From 776b118d951f99cc1e86073c18e1f8f7d110c6b6 Mon Sep 17 00:00:00 2001 From: Sophie Tyalie Date: Tue, 15 Nov 2022 16:30:52 +0100 Subject: [PATCH 1/6] CustomLock: add basic file structure This is the first commit introducing custom lock behavior. Currently does nothing, except existing and that one can reference it in the keyboard layout not dissimilar to e.g. `&none`. The code is also orientated along the latter for right now, ready to be extended. Signed-off-by: Sophie Tyalie --- app/CMakeLists.txt | 1 + app/dts/behaviors.dtsi | 3 +- app/dts/behaviors/custom_lock.dtsi | 15 +++++++ .../behaviors/zmk,behavior-custom-lock.yaml | 8 ++++ app/src/behaviors/behavior_custom_lock.c | 39 +++++++++++++++++++ 5 files changed, 65 insertions(+), 1 deletion(-) create mode 100644 app/dts/behaviors/custom_lock.dtsi create mode 100644 app/dts/bindings/behaviors/zmk,behavior-custom-lock.yaml create mode 100644 app/src/behaviors/behavior_custom_lock.c diff --git a/app/CMakeLists.txt b/app/CMakeLists.txt index 3da50b57..e611f772 100644 --- a/app/CMakeLists.txt +++ b/app/CMakeLists.txt @@ -52,6 +52,7 @@ if ((NOT CONFIG_ZMK_SPLIT) OR CONFIG_ZMK_SPLIT_ROLE_CENTRAL) target_sources(app PRIVATE src/behaviors/behavior_transparent.c) target_sources(app PRIVATE src/behaviors/behavior_none.c) target_sources(app PRIVATE src/behaviors/behavior_sensor_rotate_key_press.c) + target_sources(app PRIVATE src/behaviors/behavior_custom_lock.c) target_sources(app PRIVATE src/combo.c) target_sources(app PRIVATE src/behaviors/behavior_tap_dance.c) target_sources(app PRIVATE src/behavior_queue.c) diff --git a/app/dts/behaviors.dtsi b/app/dts/behaviors.dtsi index b3502cbb..8fa30b34 100644 --- a/app/dts/behaviors.dtsi +++ b/app/dts/behaviors.dtsi @@ -18,4 +18,5 @@ #include #include #include -#include \ No newline at end of file +#include +#include diff --git a/app/dts/behaviors/custom_lock.dtsi b/app/dts/behaviors/custom_lock.dtsi new file mode 100644 index 00000000..aa791813 --- /dev/null +++ b/app/dts/behaviors/custom_lock.dtsi @@ -0,0 +1,15 @@ +/* + * Copyright (c) 2022 The ZMK Contributors + * + * SPDX-License-Identifier: MIT + */ + +/ { + behaviors { + /omit-if-no-ref/ lock: behavior_custom_lock { + compatible = "zmk,behavior-custom-lock"; + label = "CUSTOM LOCK"; + #binding-cells = <0>; + }; + }; +}; diff --git a/app/dts/bindings/behaviors/zmk,behavior-custom-lock.yaml b/app/dts/bindings/behaviors/zmk,behavior-custom-lock.yaml new file mode 100644 index 00000000..7ea9c5dc --- /dev/null +++ b/app/dts/bindings/behaviors/zmk,behavior-custom-lock.yaml @@ -0,0 +1,8 @@ +# Copyright (c) 2022 The ZMK Contributors +# SPDX-License-Identifier: MIT + +description: Custom Lock Behavior + +compatible: "zmk,behavior-custom-lock" + +include: zero_param.yaml diff --git a/app/src/behaviors/behavior_custom_lock.c b/app/src/behaviors/behavior_custom_lock.c new file mode 100644 index 00000000..c2b82c93 --- /dev/null +++ b/app/src/behaviors/behavior_custom_lock.c @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2022 The ZMK Contributors + * + * SPDX-License-Identifier: MIT + */ + +#define DT_DRV_COMPAT zmk_behavior_custom_lock + +#include +#include +#include + +#include + +LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL); + +#if DT_HAS_COMPAT_STATUS_OKAY(DT_DRV_COMPAT) + +static int behavior_lock_init(const struct device *dev) { return 0; }; + +static int on_keymap_binding_pressed(struct zmk_behavior_binding *binding, + struct zmk_behavior_binding_event event) { + return 0; +} + +static int on_keymap_binding_released(struct zmk_behavior_binding *binding, + struct zmk_behavior_binding_event event) { + return 0; +} + +static const struct behavior_driver_api behavior_lock_driver_api = { + .binding_pressed = on_keymap_binding_pressed, + .binding_released = on_keymap_binding_released +}; + +DEVICE_DT_INST_DEFINE(0, behavior_lock_init, NULL, NULL, NULL, APPLICATION, + CONFIG_KERNEL_INIT_PRIORITY_DEFAULT, &behavior_lock_driver_api); + +#endif /* DT_HAS_COMPAT_STATUS_OKAY(DT_DRV_COMPAT) */ From dadf1e611f4a3a71607c52bbc5960f6b1e5d0a03 Mon Sep 17 00:00:00 2001 From: Sophie Tyalie Date: Thu, 17 Nov 2022 13:08:14 +0100 Subject: [PATCH 2/6] Behavior: Change way lock keys are defined in dts Previously the definition was copied almost 1:1 from the `none` behavior in order to set up the important files and have a small running, but featureless sample. This change introduces a way to actually configure the custom lock behavior, using the following syntax: ``` behaviors { dead_lock_prevent { compatible = "zmk,behavior-custom-lock"; // to define a key dead: dead_key { #binding-cells = <2>; bindings = <&kp>, <&kp>; } // or to define a macro deadm: dead_macro { #binding-cells = <2>; bindings = <¯o>, <&kp>; } } } ``` Additionally, there's now no standard definition flying around (deletion of `dts/behaviors/custom_lock.dtsi`) Signed-off-by: Sophie Tyalie --- app/dts/behaviors.dtsi | 1 - app/dts/behaviors/custom_lock.dtsi | 15 --------------- .../behaviors/zmk,behavior-custom-lock.yaml | 7 +++++-- 3 files changed, 5 insertions(+), 18 deletions(-) delete mode 100644 app/dts/behaviors/custom_lock.dtsi diff --git a/app/dts/behaviors.dtsi b/app/dts/behaviors.dtsi index 8fa30b34..58ee8359 100644 --- a/app/dts/behaviors.dtsi +++ b/app/dts/behaviors.dtsi @@ -19,4 +19,3 @@ #include #include #include -#include diff --git a/app/dts/behaviors/custom_lock.dtsi b/app/dts/behaviors/custom_lock.dtsi deleted file mode 100644 index aa791813..00000000 --- a/app/dts/behaviors/custom_lock.dtsi +++ /dev/null @@ -1,15 +0,0 @@ -/* - * Copyright (c) 2022 The ZMK Contributors - * - * SPDX-License-Identifier: MIT - */ - -/ { - behaviors { - /omit-if-no-ref/ lock: behavior_custom_lock { - compatible = "zmk,behavior-custom-lock"; - label = "CUSTOM LOCK"; - #binding-cells = <0>; - }; - }; -}; diff --git a/app/dts/bindings/behaviors/zmk,behavior-custom-lock.yaml b/app/dts/bindings/behaviors/zmk,behavior-custom-lock.yaml index 7ea9c5dc..34b67e17 100644 --- a/app/dts/bindings/behaviors/zmk,behavior-custom-lock.yaml +++ b/app/dts/bindings/behaviors/zmk,behavior-custom-lock.yaml @@ -1,8 +1,11 @@ # Copyright (c) 2022 The ZMK Contributors # SPDX-License-Identifier: MIT -description: Custom Lock Behavior +description: Custom lock behavior variable definition compatible: "zmk,behavior-custom-lock" -include: zero_param.yaml +child-binding: + description: Key definitions for lock variable + + include: two_param.yaml From 34d9eb0e961b5d74c35f02cb552e721c0bfc7238 Mon Sep 17 00:00:00 2001 From: Sophie Tyalie Date: Thu, 17 Nov 2022 16:23:11 +0100 Subject: [PATCH 3/6] Behavior: Add custom lock functionality Custom lock behavior can now be defined and actively used in the keyboard. A lock is toggled by typing a key, containing reference to the root variable definition (`compatible = "zmk,behavior-custom-lock.yaml"`). In order to have "A pressed -> Lock pressed -> A released" behave correctly, this commit introduces a queue for currently pressed keys. A release of such a key would then release the previous used behavior, instead of the behavior the current lock state would suggest. The size of the queue can be adjusted with `ZMK_BHV_LOCK_KEY_MAX_HELD` Signed-off-by: Sophie Tyalie --- app/dts/behaviors.dtsi | 2 +- .../behaviors/zmk,behavior-custom-lock.yaml | 7 + app/src/behaviors/behavior_custom_lock.c | 153 ++++++++++++++++-- 3 files changed, 145 insertions(+), 17 deletions(-) diff --git a/app/dts/behaviors.dtsi b/app/dts/behaviors.dtsi index 58ee8359..b3502cbb 100644 --- a/app/dts/behaviors.dtsi +++ b/app/dts/behaviors.dtsi @@ -18,4 +18,4 @@ #include #include #include -#include +#include \ No newline at end of file diff --git a/app/dts/bindings/behaviors/zmk,behavior-custom-lock.yaml b/app/dts/bindings/behaviors/zmk,behavior-custom-lock.yaml index 34b67e17..4cf79c5d 100644 --- a/app/dts/bindings/behaviors/zmk,behavior-custom-lock.yaml +++ b/app/dts/bindings/behaviors/zmk,behavior-custom-lock.yaml @@ -5,7 +5,14 @@ description: Custom lock behavior variable definition compatible: "zmk,behavior-custom-lock" +include: zero_param.yaml + child-binding: description: Key definitions for lock variable include: two_param.yaml + + properties: + bindings: + type: phandles + required: true diff --git a/app/src/behaviors/behavior_custom_lock.c b/app/src/behaviors/behavior_custom_lock.c index c2b82c93..944dc551 100644 --- a/app/src/behaviors/behavior_custom_lock.c +++ b/app/src/behaviors/behavior_custom_lock.c @@ -16,24 +16,145 @@ LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL); #if DT_HAS_COMPAT_STATUS_OKAY(DT_DRV_COMPAT) -static int behavior_lock_init(const struct device *dev) { return 0; }; +#define ZMK_BHV_LOCK_KEY_MAX_HELD 10 +#define ZMK_BHV_LOCK_KEY_POSITION_FREE UINT32_MAX -static int on_keymap_binding_pressed(struct zmk_behavior_binding *binding, - struct zmk_behavior_binding_event event) { - return 0; -} - -static int on_keymap_binding_released(struct zmk_behavior_binding *binding, - struct zmk_behavior_binding_event event) { - return 0; -} - -static const struct behavior_driver_api behavior_lock_driver_api = { - .binding_pressed = on_keymap_binding_pressed, - .binding_released = on_keymap_binding_released +struct behavior_custom_lock_key_config { + const struct device *dev; + char *unlocked_behavior_dev; + char *locked_behavior_dev; }; -DEVICE_DT_INST_DEFINE(0, behavior_lock_init, NULL, NULL, NULL, APPLICATION, - CONFIG_KERNEL_INIT_PRIORITY_DEFAULT, &behavior_lock_driver_api); +struct behavior_custom_lock_var_data { + bool active; +}; + +struct active_lock_key { + int layer; + uint32_t position; + struct zmk_behavior_binding binding; +}; + +struct active_lock_key active_lock_keys[ZMK_BHV_LOCK_KEY_MAX_HELD] = {}; + +static struct active_lock_key* find_lock_key(struct zmk_behavior_binding_event *event) { + for (int i = 0; i < ZMK_BHV_LOCK_KEY_MAX_HELD; i++) { + if (active_lock_keys[i].position == event->position && active_lock_keys[i].layer == event->layer) { + return &active_lock_keys[i]; + } + } + return NULL; +} + +static int new_lock_key(struct zmk_behavior_binding_event *event, struct zmk_behavior_binding binding) { + for (int i = 0; i < ZMK_BHV_LOCK_KEY_MAX_HELD; i++) { + struct active_lock_key *const ref_locks = &active_lock_keys[i]; + if (ref_locks->position == ZMK_BHV_LOCK_KEY_POSITION_FREE) { + ref_locks->position = event->position; + ref_locks->layer = event->layer; + ref_locks->binding = binding; + return 0; + } + } + return -ENOMEM; +} + +static void clear_lock_key(struct active_lock_key *lock_key) { + lock_key->position = ZMK_BHV_LOCK_KEY_POSITION_FREE; +} + +static int behavior_lock_init(const struct device *dev) { + for (int i = 0; i < ZMK_BHV_LOCK_KEY_MAX_HELD; i++) { + active_lock_keys[i].position = ZMK_BHV_LOCK_KEY_POSITION_FREE; + } + + return 0; +}; + +static int on_keymap_key_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_custom_lock_key_config *config = dev->config; + + const struct device *parent = config->dev; + const bool active = ((struct behavior_custom_lock_var_data*)parent->data)->active; + + struct zmk_behavior_binding new_binding = {0}; + if (active) { + new_binding.behavior_dev = config->locked_behavior_dev; + new_binding.param1 = binding->param2; + } else { + new_binding.behavior_dev = config->unlocked_behavior_dev; + new_binding.param1 = binding->param1; + } + + int res = new_lock_key(&event, new_binding); + if (res == -ENOMEM) { + LOG_WRN("Couldn't find space to store current lock press. Ignoring key"); + return ZMK_BEHAVIOR_OPAQUE; + } + + return behavior_keymap_binding_pressed(&new_binding, event); +} + +static int on_keymap_key_binding_released(struct zmk_behavior_binding *binding, + struct zmk_behavior_binding_event event) { + + struct active_lock_key *cur_key = find_lock_key(&event); + + if (cur_key == NULL) { + LOG_WRN("Binding for layer: %d | position: %d not found. Not sure how to proceed", event.layer, event.position); + } else { + struct zmk_behavior_binding binding = cur_key->binding; + clear_lock_key(cur_key); + return behavior_keymap_binding_released(&binding, event); + } + return ZMK_BEHAVIOR_OPAQUE; +} + +static int on_keymap_var_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_custom_lock_var_data* data = dev->data; + + data->active = !data->active; + + return ZMK_BEHAVIOR_OPAQUE; +} + +static int on_keymap_var_binding_released(struct zmk_behavior_binding *binding, + struct zmk_behavior_binding_event event) { + return ZMK_BEHAVIOR_OPAQUE; +} + +static const struct behavior_driver_api behavior_lock_key_driver_api = { + .binding_pressed = on_keymap_key_binding_pressed, + .binding_released = on_keymap_key_binding_released +}; + +static const struct behavior_driver_api behavior_lock_var_driver_api = { + .binding_pressed = on_keymap_var_binding_pressed, + .binding_released = on_keymap_var_binding_released +}; + +#define CL_CHILD(id) \ + static struct behavior_custom_lock_key_config behavior_lock_key_config_##id = { \ + .dev = DEVICE_DT_GET(DT_PARENT(id)), \ + .unlocked_behavior_dev = DT_LABEL(DT_PHANDLE_BY_IDX(id, bindings, 0)), \ + .locked_behavior_dev = DT_LABEL(DT_PHANDLE_BY_IDX(id, bindings, 1)), \ + }; \ + DEVICE_DT_DEFINE(id, &behavior_lock_init, NULL, NULL, &behavior_lock_key_config_##id, \ + APPLICATION, CONFIG_KERNEL_INIT_PRIORITY_DEFAULT, &behavior_lock_key_driver_api); + + +#define CL_INST(id) \ + DT_INST_FOREACH_CHILD(id, CL_CHILD) \ + static struct behavior_custom_lock_var_data behavior_lock_var_data_##id = { .active = false }; \ + \ + DEVICE_DT_INST_DEFINE(id, &behavior_lock_init, NULL, &behavior_lock_var_data_##id, \ + NULL, APPLICATION, CONFIG_KERNEL_INIT_PRIORITY_DEFAULT, &behavior_lock_var_driver_api); + + +DT_INST_FOREACH_STATUS_OKAY(CL_INST) #endif /* DT_HAS_COMPAT_STATUS_OKAY(DT_DRV_COMPAT) */ From aa1cc023f639b4cde32fb7212ed476dcc3f131f6 Mon Sep 17 00:00:00 2001 From: Sophie Tyalie Date: Thu, 17 Nov 2022 19:57:11 +0100 Subject: [PATCH 4/6] Behavior: Lock: Store setting across restarts Using Zephyrs settings functionality, persist the lock state across a device reset (e.g. power cycle). Settings are stored as a bool in `bhv/lock/[lock-name]`, with the lock name being the device name. I've taken the liberty to add the custom lock behavior to the `SYS_INIT` queue. Signed-off-by: Sophie Tyalie --- app/src/behaviors/behavior_custom_lock.c | 75 +++++++++++++++++++++++- 1 file changed, 72 insertions(+), 3 deletions(-) diff --git a/app/src/behaviors/behavior_custom_lock.c b/app/src/behaviors/behavior_custom_lock.c index 944dc551..175cb129 100644 --- a/app/src/behaviors/behavior_custom_lock.c +++ b/app/src/behaviors/behavior_custom_lock.c @@ -7,6 +7,7 @@ #define DT_DRV_COMPAT zmk_behavior_custom_lock #include +#include #include #include @@ -63,7 +64,72 @@ static void clear_lock_key(struct active_lock_key *lock_key) { lock_key->position = ZMK_BHV_LOCK_KEY_POSITION_FREE; } -static int behavior_lock_init(const struct device *dev) { +#if IS_ENABLED(CONFIG_SETTINGS) + +static void lock_save_state(const char* name, bool active) { + char settings_name[30]; + sprintf(settings_name, "bhv/lock/%s", name); + settings_save_one(settings_name, &active, sizeof(bool)); +} + +static void lock_delete_state(const char* name) { + char settings_name[30]; + sprintf(settings_name, "bhv/lock/%s", name); + settings_delete(settings_name); +} + +static int lock_settings_load(const char *name, size_t len, settings_read_cb read_cb, void *cb_arg) { + const struct device* dev = device_get_binding(name); + + if (dev == NULL) { + LOG_WRN("Unknown lock device from settings %s - purging from settings", name); + lock_delete_state(name); + return -1; + } + + struct behavior_custom_lock_var_data* data = dev->data; + int rc; + + if (len != sizeof(bool)) { + LOG_DBG("something is of with size %d", len); + return -EINVAL; + } + + rc = read_cb(cb_arg, &data->active, sizeof(bool)); + if (rc >= 0) { + return 0; + } + + return rc; +} + +struct settings_handler lock_settings_conf = {.name = "bhv/lock", .h_set = lock_settings_load}; + +#endif + +static int behavior_lock_init(const struct device *_arg) { + +#if IS_ENABLED(CONFIG_SETTINGS) + settings_subsys_init(); + + int err = settings_register(&lock_settings_conf); + if (err) { + LOG_ERR("Failed to register the ext_power settings handler (err %d)", err); + return err; + } + + settings_load_subtree("bhv/lock"); +#endif + + return 0; +} + + +static int behavior_lock_key_init(const struct device *dev) { + return 0; +}; + +static int behavior_lock_var_init(const struct device *dev) { for (int i = 0; i < ZMK_BHV_LOCK_KEY_MAX_HELD; i++) { active_lock_keys[i].position = ZMK_BHV_LOCK_KEY_POSITION_FREE; } @@ -118,6 +184,7 @@ static int on_keymap_var_binding_pressed(struct zmk_behavior_binding *binding, struct behavior_custom_lock_var_data* data = dev->data; data->active = !data->active; + lock_save_state(dev->name, data->active); return ZMK_BEHAVIOR_OPAQUE; } @@ -143,7 +210,7 @@ static const struct behavior_driver_api behavior_lock_var_driver_api = { .unlocked_behavior_dev = DT_LABEL(DT_PHANDLE_BY_IDX(id, bindings, 0)), \ .locked_behavior_dev = DT_LABEL(DT_PHANDLE_BY_IDX(id, bindings, 1)), \ }; \ - DEVICE_DT_DEFINE(id, &behavior_lock_init, NULL, NULL, &behavior_lock_key_config_##id, \ + DEVICE_DT_DEFINE(id, &behavior_lock_key_init, NULL, NULL, &behavior_lock_key_config_##id, \ APPLICATION, CONFIG_KERNEL_INIT_PRIORITY_DEFAULT, &behavior_lock_key_driver_api); @@ -151,10 +218,12 @@ static const struct behavior_driver_api behavior_lock_var_driver_api = { DT_INST_FOREACH_CHILD(id, CL_CHILD) \ static struct behavior_custom_lock_var_data behavior_lock_var_data_##id = { .active = false }; \ \ - DEVICE_DT_INST_DEFINE(id, &behavior_lock_init, NULL, &behavior_lock_var_data_##id, \ + DEVICE_DT_INST_DEFINE(id, &behavior_lock_var_init, NULL, &behavior_lock_var_data_##id, \ NULL, APPLICATION, CONFIG_KERNEL_INIT_PRIORITY_DEFAULT, &behavior_lock_var_driver_api); DT_INST_FOREACH_STATUS_OKAY(CL_INST) +SYS_INIT(behavior_lock_init, APPLICATION, CONFIG_APPLICATION_INIT_PRIORITY); + #endif /* DT_HAS_COMPAT_STATUS_OKAY(DT_DRV_COMPAT) */ From cac33f493aeeac7a7e5e235d283e37a6a6682baf Mon Sep 17 00:00:00 2001 From: Sophie Tyalie Date: Thu, 17 Nov 2022 21:04:37 +0100 Subject: [PATCH 5/6] Behavior: Lock: Make persistence configurable Persistence of a custom lock is disabled by default, but it can now be enabled in the dts keyboard config similar to this: ``` deadlock: deadlock { compatible = "zmk,behavior-custom-lock"; label = "dead-key-toggle"; #binding-cells = <0>; persistence = "enabled"; } ``` Currently the options are `disabled`, `enabled` and `per-profile`, where the latter doesn't do anything different to `enabled` as of right now. Signed-off-by: Sophie Tyalie --- .../behaviors/zmk,behavior-custom-lock.yaml | 10 +++++++ app/src/behaviors/behavior_custom_lock.c | 27 ++++++++++++++++--- 2 files changed, 33 insertions(+), 4 deletions(-) diff --git a/app/dts/bindings/behaviors/zmk,behavior-custom-lock.yaml b/app/dts/bindings/behaviors/zmk,behavior-custom-lock.yaml index 4cf79c5d..19fa0225 100644 --- a/app/dts/bindings/behaviors/zmk,behavior-custom-lock.yaml +++ b/app/dts/bindings/behaviors/zmk,behavior-custom-lock.yaml @@ -7,6 +7,16 @@ compatible: "zmk,behavior-custom-lock" include: zero_param.yaml +properties: + persistence: + type: string + default: "disabled" + required: false + enum: + - "disabled" + - "enabled" + - "per-profile" + child-binding: description: Key definitions for lock variable diff --git a/app/src/behaviors/behavior_custom_lock.c b/app/src/behaviors/behavior_custom_lock.c index 175cb129..31c83ac4 100644 --- a/app/src/behaviors/behavior_custom_lock.c +++ b/app/src/behaviors/behavior_custom_lock.c @@ -26,6 +26,10 @@ struct behavior_custom_lock_key_config { char *locked_behavior_dev; }; +struct behavior_custom_lock_var_config { + uint8_t persistence; +}; + struct behavior_custom_lock_var_data { bool active; }; @@ -88,8 +92,15 @@ static int lock_settings_load(const char *name, size_t len, settings_read_cb rea } struct behavior_custom_lock_var_data* data = dev->data; + const struct behavior_custom_lock_var_config *config = dev->config; int rc; + if (config->persistence == 0) { + LOG_WRN("key %s is not marked as persistent (%d), deleting", name, config->persistence); + lock_delete_state(name); + return 0; + } + if (len != sizeof(bool)) { LOG_DBG("something is of with size %d", len); return -EINVAL; @@ -182,9 +193,13 @@ static int on_keymap_var_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_custom_lock_var_data* data = dev->data; + const struct behavior_custom_lock_var_config* config = dev->config; data->active = !data->active; - lock_save_state(dev->name, data->active); + + if (config->persistence != 0) { + lock_save_state(dev->name, data->active); + } return ZMK_BEHAVIOR_OPAQUE; } @@ -217,9 +232,13 @@ static const struct behavior_driver_api behavior_lock_var_driver_api = { #define CL_INST(id) \ DT_INST_FOREACH_CHILD(id, CL_CHILD) \ static struct behavior_custom_lock_var_data behavior_lock_var_data_##id = { .active = false }; \ - \ - DEVICE_DT_INST_DEFINE(id, &behavior_lock_var_init, NULL, &behavior_lock_var_data_##id, \ - NULL, APPLICATION, CONFIG_KERNEL_INIT_PRIORITY_DEFAULT, &behavior_lock_var_driver_api); + static struct behavior_custom_lock_var_config behavior_lock_var_config_##id = { \ + .persistence = DT_INST_ENUM_IDX(id, persistence), \ + };\ + \ + DEVICE_DT_INST_DEFINE(id, &behavior_lock_var_init, NULL, \ + &behavior_lock_var_data_##id, &behavior_lock_var_config_##id, \ + APPLICATION, CONFIG_KERNEL_INIT_PRIORITY_DEFAULT, &behavior_lock_var_driver_api); DT_INST_FOREACH_STATUS_OKAY(CL_INST) From b4eac9da30a9edacabdb5d9bfead753ad8a0a1bf Mon Sep 17 00:00:00 2001 From: Sophie Tyalie Date: Thu, 17 Nov 2022 21:41:00 +0100 Subject: [PATCH 6/6] Behavior: Lock: Add per-profile persistence Lock state will be persistently stored on a per profile basis if configured in keyboard dts (`persistence = "per-profile"`). If per-profile is enabled the settings will store a state for USB and the maximum allowed connection profiles. Signed-off-by: Sophie Tyalie --- app/src/behaviors/behavior_custom_lock.c | 66 ++++++++++++++++++++---- 1 file changed, 56 insertions(+), 10 deletions(-) diff --git a/app/src/behaviors/behavior_custom_lock.c b/app/src/behaviors/behavior_custom_lock.c index 31c83ac4..bc973d53 100644 --- a/app/src/behaviors/behavior_custom_lock.c +++ b/app/src/behaviors/behavior_custom_lock.c @@ -11,6 +11,8 @@ #include #include +#include +#include #include LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL); @@ -20,6 +22,12 @@ LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL); #define ZMK_BHV_LOCK_KEY_MAX_HELD 10 #define ZMK_BHV_LOCK_KEY_POSITION_FREE UINT32_MAX +enum lock_persistence { + PERS_DISABLED = 0, + PERS_ENABLED = 1, + PERS_PER_PROFILE = 2 +}; + struct behavior_custom_lock_key_config { const struct device *dev; char *unlocked_behavior_dev; @@ -27,11 +35,16 @@ struct behavior_custom_lock_key_config { }; struct behavior_custom_lock_var_config { - uint8_t persistence; + enum lock_persistence persistence; +}; + +struct active_state { + bool def; + bool profiles[ZMK_BLE_PROFILE_COUNT]; }; struct behavior_custom_lock_var_data { - bool active; + struct active_state active; }; struct active_lock_key { @@ -68,12 +81,44 @@ static void clear_lock_key(struct active_lock_key *lock_key) { lock_key->position = ZMK_BHV_LOCK_KEY_POSITION_FREE; } +static bool* get_active_state(const struct device* dev) { + struct behavior_custom_lock_var_data* data = dev->data; + const struct behavior_custom_lock_var_config *config = dev->config; + + switch (config->persistence) { + case PERS_DISABLED: + case PERS_ENABLED: + return &data->active.def; + case PERS_PER_PROFILE: + if (zmk_endpoints_selected() == ZMK_ENDPOINT_BLE) { + return &data->active.profiles[zmk_ble_active_profile_index()]; + } else { + return &data->active.def; + } + default: + LOG_ERR("Unknown persistence value encountered %d", config->persistence); + } + + LOG_ERR("Couldn't find usable active state"); + return NULL; +} + +static void toggle_active_state(const struct device* dev) { + bool* state = get_active_state(dev); + if (state == NULL) { + LOG_ERR("encountered null state %s", dev->name); + return; + } + + *state = !(*state); +} + #if IS_ENABLED(CONFIG_SETTINGS) -static void lock_save_state(const char* name, bool active) { +static void lock_save_state(const char* name, struct active_state* active) { char settings_name[30]; sprintf(settings_name, "bhv/lock/%s", name); - settings_save_one(settings_name, &active, sizeof(bool)); + settings_save_one(settings_name, active, sizeof(struct active_state)); } static void lock_delete_state(const char* name) { @@ -101,12 +146,12 @@ static int lock_settings_load(const char *name, size_t len, settings_read_cb rea return 0; } - if (len != sizeof(bool)) { + if (len != sizeof(struct active_state)) { LOG_DBG("something is of with size %d", len); return -EINVAL; } - rc = read_cb(cb_arg, &data->active, sizeof(bool)); + rc = read_cb(cb_arg, &data->active, sizeof(struct active_state)); if (rc >= 0) { return 0; } @@ -154,10 +199,9 @@ static int on_keymap_key_binding_pressed(struct zmk_behavior_binding *binding, const struct behavior_custom_lock_key_config *config = dev->config; const struct device *parent = config->dev; - const bool active = ((struct behavior_custom_lock_var_data*)parent->data)->active; struct zmk_behavior_binding new_binding = {0}; - if (active) { + if (*get_active_state(parent)) { new_binding.behavior_dev = config->locked_behavior_dev; new_binding.param1 = binding->param2; } else { @@ -195,11 +239,13 @@ static int on_keymap_var_binding_pressed(struct zmk_behavior_binding *binding, struct behavior_custom_lock_var_data* data = dev->data; const struct behavior_custom_lock_var_config* config = dev->config; - data->active = !data->active; + toggle_active_state(dev); +#if IS_ENABLED(CONFIG_SETTINGS) if (config->persistence != 0) { - lock_save_state(dev->name, data->active); + lock_save_state(dev->name, &data->active); } +#endif return ZMK_BEHAVIOR_OPAQUE; }