diff --git a/app/CMakeLists.txt b/app/CMakeLists.txt index 6ef00311..f0b5d82a 100644 --- a/app/CMakeLists.txt +++ b/app/CMakeLists.txt @@ -49,6 +49,7 @@ if ((NOT CONFIG_ZMK_SPLIT) OR CONFIG_ZMK_SPLIT_ROLE_CENTRAL) target_sources(app PRIVATE src/behaviors/behavior_key_repeat.c) target_sources_ifdef(CONFIG_ZMK_BEHAVIOR_MACRO app PRIVATE src/behaviors/behavior_macro.c) target_sources(app PRIVATE src/behaviors/behavior_momentary_layer.c) + target_sources(app PRIVATE src/behaviors/behavior_momentary_layer_lock.c) target_sources(app PRIVATE src/behaviors/behavior_mod_morph.c) target_sources(app PRIVATE src/behaviors/behavior_outputs.c) target_sources(app PRIVATE src/behaviors/behavior_toggle_layer.c) diff --git a/app/dts/behaviors.dtsi b/app/dts/behaviors.dtsi index 23f2fee2..2409cf1e 100644 --- a/app/dts/behaviors.dtsi +++ b/app/dts/behaviors.dtsi @@ -7,6 +7,7 @@ #include #include #include +#include #include #include #include diff --git a/app/dts/behaviors/momentary_layer_lock.dtsi b/app/dts/behaviors/momentary_layer_lock.dtsi new file mode 100644 index 00000000..792c2195 --- /dev/null +++ b/app/dts/behaviors/momentary_layer_lock.dtsi @@ -0,0 +1,15 @@ +/* + * Copyright (c) 2023 The ZMK Contributors + * + * SPDX-License-Identifier: MIT + */ + +/ { + behaviors { + /omit-if-no-ref/ molock: behavior_momentary_layer_lock { + compatible = "zmk,behavior-momentary-layer-lock"; + label = "MO_LOCK"; + #binding-cells = <1>; + }; + }; +}; diff --git a/app/dts/bindings/behaviors/zmk,behavior-momentary-layer-lock.yaml b/app/dts/bindings/behaviors/zmk,behavior-momentary-layer-lock.yaml new file mode 100644 index 00000000..9d32afc1 --- /dev/null +++ b/app/dts/bindings/behaviors/zmk,behavior-momentary-layer-lock.yaml @@ -0,0 +1,8 @@ +# Copyright (c) 2023 The ZMK Contributors +# SPDX-License-Identifier: MIT + +description: Momentary layer on press/release behavior + +compatible: "zmk,behavior-momentary-layer-lock" + +include: one_param.yaml diff --git a/app/include/zmk/momentary_layer.h b/app/include/zmk/momentary_layer.h new file mode 100644 index 00000000..7511c0b1 --- /dev/null +++ b/app/include/zmk/momentary_layer.h @@ -0,0 +1,15 @@ +/* + * Copyright (c) 2023 The ZMK Contributors + * + * SPDX-License-Identifier: MIT + */ + +#pragma once + +#include + +// Locks all active momentary layers so they are not disabled when the key is +// released. +// +// Returns a set of the layers that were locked. +zmk_keymap_layers_state_t zmk_lock_active_momentary_layers(); \ No newline at end of file diff --git a/app/src/behaviors/behavior_momentary_layer.c b/app/src/behaviors/behavior_momentary_layer.c index 0c86e605..9f708f7b 100644 --- a/app/src/behaviors/behavior_momentary_layer.c +++ b/app/src/behaviors/behavior_momentary_layer.c @@ -12,32 +12,51 @@ #include #include +#include LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL); struct behavior_mo_config {}; -struct behavior_mo_data {}; +struct behavior_mo_data { + zmk_keymap_layers_state_t active_momentary_layers; + zmk_keymap_layers_state_t ignore_on_release; +}; + +static const struct behavior_mo_config behavior_mo_config = {}; +static struct behavior_mo_data behavior_mo_data; + +zmk_keymap_layers_state_t zmk_lock_active_momentary_layers() { + return behavior_mo_data.ignore_on_release = behavior_mo_data.active_momentary_layers; +} static int behavior_mo_init(const struct device *dev) { return 0; }; static int mo_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_activate(binding->param1); + int layer = binding->param1; + LOG_DBG("position %d layer %d", event.position, layer); + WRITE_BIT(behavior_mo_data.active_momentary_layers, layer, true); + return zmk_keymap_layer_activate(layer); } static int mo_keymap_binding_released(struct zmk_behavior_binding *binding, struct zmk_behavior_binding_event event) { - LOG_DBG("position %d layer %d", event.position, binding->param1); + int layer = binding->param1; + LOG_DBG("position %d layer %d", event.position, layer); + WRITE_BIT(behavior_mo_data.active_momentary_layers, layer, false); + + // If the layer is locked, don't deactivate it. Instead clear the + // ignore_on_release flag so the next press/release will. + if (behavior_mo_data.ignore_on_release & BIT(layer)) { + WRITE_BIT(behavior_mo_data.ignore_on_release, layer, false); + return 0; + } + return zmk_keymap_layer_deactivate(binding->param1); } static const struct behavior_driver_api behavior_mo_driver_api = { .binding_pressed = mo_keymap_binding_pressed, .binding_released = mo_keymap_binding_released}; -static const struct behavior_mo_config behavior_mo_config = {}; - -static struct behavior_mo_data behavior_mo_data; - BEHAVIOR_DT_INST_DEFINE(0, behavior_mo_init, NULL, &behavior_mo_data, &behavior_mo_config, - POST_KERNEL, CONFIG_KERNEL_INIT_PRIORITY_DEFAULT, &behavior_mo_driver_api); + POST_KERNEL, CONFIG_KERNEL_INIT_PRIORITY_DEFAULT, &behavior_mo_driver_api); \ No newline at end of file diff --git a/app/src/behaviors/behavior_momentary_layer_lock.c b/app/src/behaviors/behavior_momentary_layer_lock.c new file mode 100644 index 00000000..b2d03792 --- /dev/null +++ b/app/src/behaviors/behavior_momentary_layer_lock.c @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2023 The ZMK Contributors + * + * SPDX-License-Identifier: MIT + */ + +#define DT_DRV_COMPAT zmk_behavior_momentary_layer_lock + +#include +#include +#include + +#include +#include +#include + +LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL); + +#if DT_HAS_COMPAT_STATUS_OKAY(DT_DRV_COMPAT) + +static int behavior_mo_lock_init(const struct device *dev) { return 0; }; + +static int mo_lock_keymap_binding_pressed(struct zmk_behavior_binding *binding, + struct zmk_behavior_binding_event event) { + int fallback_layer = binding->param1; + zmk_keymap_layers_state_t locked = zmk_lock_active_momentary_layers(); + if (!locked && fallback_layer >= 0) { + return zmk_keymap_layer_to(fallback_layer); + } + + return 0; +} + +static const struct behavior_driver_api behavior_mo_lock_driver_api = { + .binding_pressed = mo_lock_keymap_binding_pressed}; + +DEVICE_DT_INST_DEFINE(0, behavior_mo_lock_init, NULL, NULL, NULL, APPLICATION, + CONFIG_KERNEL_INIT_PRIORITY_DEFAULT, &behavior_mo_lock_driver_api); + +#endif /* DT_HAS_COMPAT_STATUS_OKAY(DT_DRV_COMPAT) */ \ No newline at end of file