diff --git a/app/CMakeLists.txt b/app/CMakeLists.txt index 793f386d..c800d1cc 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_outputs.c) target_sources(app PRIVATE src/behaviors/behavior_toggle_layer.c) target_sources(app PRIVATE src/behaviors/behavior_to_layer.c) + target_sources(app PRIVATE src/behaviors/behavior_base_layer.c) target_sources(app PRIVATE src/behaviors/behavior_transparent.c) target_sources(app PRIVATE src/behaviors/behavior_none.c) target_sources_ifdef(CONFIG_ZMK_BEHAVIOR_SENSOR_ROTATE app PRIVATE src/behaviors/behavior_sensor_rotate.c) diff --git a/app/dts/behaviors.dtsi b/app/dts/behaviors.dtsi index b3502cbb..83faeacd 100644 --- a/app/dts/behaviors.dtsi +++ b/app/dts/behaviors.dtsi @@ -9,6 +9,7 @@ #include #include #include +#include #include #include #include @@ -18,4 +19,4 @@ #include #include #include -#include \ No newline at end of file +#include diff --git a/app/dts/behaviors/base_layer.dtsi b/app/dts/behaviors/base_layer.dtsi new file mode 100644 index 00000000..f9e60729 --- /dev/null +++ b/app/dts/behaviors/base_layer.dtsi @@ -0,0 +1,15 @@ +/* + * Copyright (c) 2022 The ZMK Contributors + * + * SPDX-License-Identifier: MIT + */ + +/ { + behaviors { + /omit-if-no-ref/ base: behavior_base_layer { + compatible = "zmk,behavior-base-layer"; + label = "BASE_LAYER"; + #binding-cells = <1>; + }; + }; +}; diff --git a/app/dts/bindings/behaviors/zmk,behavior-base-layer.yaml b/app/dts/bindings/behaviors/zmk,behavior-base-layer.yaml new file mode 100644 index 00000000..91c3809b --- /dev/null +++ b/app/dts/bindings/behaviors/zmk,behavior-base-layer.yaml @@ -0,0 +1,8 @@ +# Copyright (c) 2022 The ZMK Contributors +# SPDX-License-Identifier: MIT + +description: Base Layer + +compatible: "zmk,behavior-base-layer" + +include: one_param.yaml diff --git a/app/src/behaviors/behavior_base_layer.c b/app/src/behaviors/behavior_base_layer.c new file mode 100644 index 00000000..ffce6e74 --- /dev/null +++ b/app/src/behaviors/behavior_base_layer.c @@ -0,0 +1,126 @@ +/* + * Copyright (c) 2023 The ZMK Contributors + * + * SPDX-License-Identifier: MIT + */ + +#define DT_DRV_COMPAT zmk_behavior_base_layer + +#include +#include + +#include +#include +#include +#include + +#if IS_ENABLED(CONFIG_SETTINGS) +#include +#endif + +#include +LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL); + +#if DT_HAS_COMPAT_STATUS_OKAY(DT_DRV_COMPAT) + +struct base_layer_state { + uint8_t layer_by_enpoint[ZMK_ENDPOINT_COUNT]; +}; + +static struct base_layer_state state; + +#if IS_ENABLED(CONFIG_SETTINGS) +static int base_layer_settings_set(const char *name, size_t len, settings_read_cb read_cb, + void *cb_arg) { + const char *next; + if (settings_name_steq(name, "state", &next) && !next) { + if (len != sizeof(state)) { + return -EINVAL; + } + return MIN(read_cb(cb_arg, &state, sizeof(state)), 0); + } + return -ENOENT; +} + +static void base_layer_save_work_handler(struct k_work *work) { + settings_save_one("base_layer/state", &state, sizeof(state)); +} + +static struct k_work_delayable base_layer_save_work; +struct settings_handler base_layer_settings_handler = {.name = "base_layer", + .h_set = base_layer_settings_set}; +#endif /* IS_ENABLED(CONFIG_SETTINGS) */ + +static int behavior_base_layer_init(const struct device *dev) { +#if IS_ENABLED(CONFIG_SETTINGS) + settings_subsys_init(); + + int err = settings_register(&base_layer_settings_handler); + if (err) { + LOG_ERR("Failed to register the base_layer settings handler (err %d)", err); + return err; + } + + k_work_init_delayable(&base_layer_save_work, base_layer_save_work_handler); + + settings_load_subtree("base_layer"); +#endif /* IS_ENABLED(CONFIG_SETTINGS) */ + + return 0; +}; + +static int on_keymap_binding_pressed(struct zmk_behavior_binding *binding, + struct zmk_behavior_binding_event event) { + LOG_DBG("position %d layer %d", event.position, binding->param1); + + const struct zmk_endpoint_instance endpoint = zmk_endpoints_selected(); + const int endpoint_index = zmk_endpoint_instance_to_index(endpoint); + const uint8_t layer = binding->param1; + + state.layer_by_enpoint[endpoint_index] = layer; + zmk_keymap_layer_to(layer); + + char endpoint_str[ZMK_ENDPOINT_STR_LEN]; + zmk_endpoint_instance_to_str(endpoint, endpoint_str, sizeof(endpoint_str)); + LOG_INF("saved base layer %d for endpoint %s", layer, endpoint_str); + +#if IS_ENABLED(CONFIG_SETTINGS) + k_work_reschedule(&base_layer_save_work, K_MSEC(CONFIG_ZMK_SETTINGS_SAVE_DEBOUNCE)); +#endif /* IS_ENABLED(CONFIG_SETTINGS) */ + + return ZMK_BEHAVIOR_OPAQUE; +} + +static int on_keymap_binding_released(struct zmk_behavior_binding *binding, + struct zmk_behavior_binding_event event) { + LOG_DBG("position %d layer %d", event.position, binding->param1); + return ZMK_BEHAVIOR_OPAQUE; +} + +static int base_layer_listener(const zmk_event_t *e) { + struct zmk_endpoint_changed *data = as_zmk_endpoint_changed(e); + if (data != NULL) { + const int endpoint_index = zmk_endpoint_instance_to_index(data->endpoint); + const uint8_t layer = state.layer_by_enpoint[endpoint_index]; + zmk_keymap_layer_to(layer); + + char endpoint_str[ZMK_ENDPOINT_STR_LEN]; + zmk_endpoint_instance_to_str(data->endpoint, endpoint_str, sizeof(endpoint_str)); + LOG_INF("restored base layer %d for endpoint %s", layer, endpoint_str); + } + + return ZMK_EV_EVENT_BUBBLE; +} +static ZMK_LISTENER(base_layer_listener, base_layer_listener); + +static ZMK_SUBSCRIPTION(base_layer_listener, zmk_endpoint_changed); + +static const struct behavior_driver_api behavior_base_layer_driver_api = { + .binding_pressed = on_keymap_binding_pressed, + .binding_released = on_keymap_binding_released, +}; + +DEVICE_DT_INST_DEFINE(0, behavior_base_layer_init, NULL, NULL, NULL, APPLICATION, + CONFIG_KERNEL_INIT_PRIORITY_DEFAULT, &behavior_base_layer_driver_api); + +#endif /* DT_HAS_COMPAT_STATUS_OKAY(DT_DRV_COMPAT) */