This commit is contained in:
Pablo Martínez 2024-08-19 15:30:06 +02:00 committed by GitHub
commit 8d31b0e849
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
10 changed files with 241 additions and 1 deletions

View file

@ -59,6 +59,7 @@ if ((NOT CONFIG_ZMK_SPLIT) OR CONFIG_ZMK_SPLIT_ROLE_CENTRAL)
target_sources(app PRIVATE src/behaviors/behavior_to_layer.c)
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_default_layer.c)
target_sources_ifdef(CONFIG_ZMK_BEHAVIOR_SENSOR_ROTATE app PRIVATE src/behaviors/behavior_sensor_rotate.c)
target_sources_ifdef(CONFIG_ZMK_BEHAVIOR_SENSOR_ROTATE_VAR app PRIVATE src/behaviors/behavior_sensor_rotate_var.c)
target_sources_ifdef(CONFIG_ZMK_BEHAVIOR_SENSOR_ROTATE_COMMON app PRIVATE src/behaviors/behavior_sensor_rotate_common.c)

View file

@ -22,3 +22,4 @@
#include <behaviors/mouse_key_press.dtsi>
#include <behaviors/soft_off.dtsi>
#include <behaviors/studio_unlock.dtsi>
#include <behaviors/default_layer.dtsi>

View file

@ -0,0 +1,14 @@
/*
* Copyright (c) 2024 The ZMK Contributors
*
* SPDX-License-Identifier: MIT
*/
/ {
behaviors {
/omit-if-no-ref/ df: default_layer {
compatible = "zmk,behavior-default-layer";
#binding-cells = <1>;
};
};
};

View file

@ -0,0 +1,8 @@
# Copyright (c) 2024 The ZMK Contributors
# SPDX-License-Identifier: MIT
description: Behavior to change default layer for current endpoint
compatible: "zmk,behavior-default-layer"
include: one_param.yaml

View file

@ -15,6 +15,7 @@
typedef uint32_t zmk_keymap_layers_state_t;
uint8_t zmk_keymap_layer_default(void);
int zmk_keymap_layer_set_default(uint8_t layer);
zmk_keymap_layers_state_t zmk_keymap_layer_state(void);
bool zmk_keymap_layer_active(uint8_t layer);
uint8_t zmk_keymap_highest_layer_active(void);

View file

@ -0,0 +1,158 @@
/*
* Copyright (c) 2024 The ZMK Contributors
*
* SPDX-License-Identifier: MIT
*/
#define DT_DRV_COMPAT zmk_behavior_default_layer
#include <zephyr/device.h>
#include <drivers/behavior.h>
#include <zephyr/logging/log.h>
#include <zephyr/settings/settings.h>
#include <zmk/behavior.h>
#include <zmk/endpoints.h>
#include <zmk/keymap.h>
#include <zmk/event_manager.h>
#include <zmk/events/endpoint_changed.h>
LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL);
#if DT_HAS_COMPAT_STATUS_OKAY(DT_DRV_COMPAT)
struct default_layer_settings_t {
bool using_global_setting;
uint8_t global_default;
uint8_t endpoint_defaults[ZMK_ENDPOINT_COUNT];
};
static struct default_layer_settings_t default_layers = {0};
static struct k_work_delayable df_layers_save_work;
static void zmk_default_layers_save_state_work(struct k_work *_work) {
settings_save_one("default_layer/settings", &default_layers, sizeof(default_layers));
}
static int apply_default_layer_config(struct zmk_endpoint_instance endpoint) {
uint8_t index = zmk_endpoint_instance_to_index(endpoint);
uint8_t layer = default_layers.endpoint_defaults[index];
int ret = zmk_keymap_layer_set_default(layer);
if (ret < 0) {
LOG_WRN("Could not apply default layer from settings. Perhaps something in the code/keymap "
"changed since configuration was saved.");
return ret;
}
LOG_INF("Activated default layer (%d) for the current endpoint.", layer);
return 0;
}
static int default_layer_set(const char *name, size_t len, settings_read_cb read_cb, void *cb_arg) {
const char *next;
int rc;
if (settings_name_steq(name, "settings", &next) && !next) {
if (len != sizeof(default_layers)) {
return -EINVAL;
}
rc = read_cb(cb_arg, &default_layers, sizeof(default_layers));
if (rc >= 0) {
return 0;
}
return rc;
}
return -ENOENT;
}
struct settings_handler default_layer_conf = {
.name = "default_layer",
.h_set = default_layer_set,
};
static int default_layer_init(void) {
settings_subsys_init();
int ret = settings_register(&default_layer_conf);
if (ret) {
LOG_ERR("Could not register default layer settings (%d).", ret);
return ret;
}
k_work_init_delayable(&df_layers_save_work, zmk_default_layers_save_state_work);
settings_load_subtree("default_layer");
return apply_default_layer_config(zmk_endpoints_selected());
}
SYS_INIT(default_layer_init, APPLICATION, CONFIG_APPLICATION_INIT_PRIORITY);
static int save_default_layer_setting(uint8_t layer, struct zmk_endpoint_instance endpoint) {
if (layer >= ZMK_KEYMAP_LAYERS_LEN) {
return -EINVAL;
}
uint8_t index = zmk_endpoint_instance_to_index(endpoint);
default_layers.endpoint_defaults[index] = layer;
char endpoint_str[ZMK_ENDPOINT_STR_LEN];
zmk_endpoint_instance_to_str(endpoint, endpoint_str, sizeof(endpoint_str));
LOG_INF("Updated default layer (%d) for %s.", layer, endpoint_str);
int ret = k_work_reschedule(&df_layers_save_work, K_MSEC(CONFIG_ZMK_SETTINGS_SAVE_DEBOUNCE));
return MIN(0, ret);
}
static int behavior_default_layer_init(const struct device *dev) { return 0; }
static int on_keymap_binding_pressed(struct zmk_behavior_binding *binding,
struct zmk_behavior_binding_event event) {
int ret = 0;
struct zmk_endpoint_instance endpoint = zmk_endpoints_selected();
ret = save_default_layer_setting(binding->param1, endpoint);
if (ret < 0) {
return ret;
}
ret = apply_default_layer_config(endpoint);
if (ret < 0) {
return ret;
}
return 0;
}
static int on_keymap_binding_released(struct zmk_behavior_binding *binding,
struct zmk_behavior_binding_event event) {
return ZMK_BEHAVIOR_OPAQUE;
}
static const struct behavior_driver_api behavior_default_layer_driver_api = {
.binding_pressed = on_keymap_binding_pressed,
.binding_released = on_keymap_binding_released,
};
BEHAVIOR_DT_INST_DEFINE(0, behavior_default_layer_init, NULL, NULL, NULL, POST_KERNEL,
CONFIG_KERNEL_INIT_PRIORITY_DEFAULT, &behavior_default_layer_driver_api);
#endif
static int endpoint_changed_cb(const zmk_event_t *eh) {
struct zmk_endpoint_changed *evt = as_zmk_endpoint_changed(eh);
if (evt != NULL) {
apply_default_layer_config(evt->endpoint);
}
return ZMK_EV_EVENT_BUBBLE;
}
ZMK_LISTENER(endpoint, endpoint_changed_cb);
ZMK_SUBSCRIPTION(endpoint, zmk_endpoint_changed);

View file

@ -108,6 +108,29 @@ static inline int set_layer_state(uint8_t layer, bool state) {
uint8_t zmk_keymap_layer_default(void) { return _zmk_keymap_layer_default; }
int zmk_keymap_layer_set_default(uint8_t layer) {
int ret = 0;
uint8_t prev_default = _zmk_keymap_layer_default;
ret = set_layer_state(layer, true);
if (ret < 0) {
LOG_WRN("Failed to activate the new default layer; aborting changes.");
return ret;
}
_zmk_keymap_layer_default = layer;
ret = set_layer_state(prev_default, false);
if (ret < 0) {
LOG_WRN("Unable to deactivate the current default layer; reverting changes.");
_zmk_keymap_layer_default = prev_default;
set_layer_state(layer, false);
return ret;
}
LOG_DBG("Default layer changed to: %d", layer);
return 0;
}
zmk_keymap_layers_state_t zmk_keymap_layer_state(void) { return _zmk_keymap_layer_state; }
bool zmk_keymap_layer_active_with_state(uint8_t layer, zmk_keymap_layers_state_t state_to_test) {

View file

@ -37,6 +37,7 @@ Below is a summary of pre-defined behavior bindings and user-definable behaviors
| `&to` | [To Layer](layers.md#to-layer) | Enables a layer and disables all other layers except the default layer |
| `&tog` | [Toggle Layer](layers.md#toggle-layer) | Enables a layer until the layer is manually disabled |
| `&sl` | [Sticky Layer](sticky-layer.md) | Activates a layer until another key is pressed, then deactivates it |
| `&df` | [Default Layer](layers.md#default-layer) | Change the default layer. Persisted across power reset. |
## Mouse Emulation Behaviors

View file

@ -120,3 +120,36 @@ Example:
The "conditional layers" feature enables a particular layer when all layers in a specified set are active.
For more information, see [conditional layers](../features/conditional-layers.md).
## Default Layer
?> What is the default layer?
It is the first one you define on your keymap (unless changed using this behavior). It is _special_ in two aspects:
- It can't be disabled by other behaviors.
- It is the only one active when the board starts running.
This behavior allows configuring a different default layer, for example to test DVORAK while keeping QWERTY on another layer, or moving a couple keycodes around for Windows/Mac usage.
This setting is stored on a per-endpoint basis, so you can configure USB to use QWERTY, and the first BLE endpoint to use DVORAK.
The stored settings are read and applied when the keyboard boots (receives powers) and also when the selected endpoint changes.
### Behavior Binding
- Reference: `&df`
- Parameter: The layer number to set as default for current endpoint, e.g. `1`
Example:
```dts
&df DVORAK
```
For a keymap with:
```dts
#define QWERTY 0
#define DVORAK 1
```

View file

@ -13,7 +13,7 @@ Applies to: `compatible = "zmk,keymap"`
Definition file: [zmk/app/dts/bindings/zmk,keymap.yaml](https://github.com/zmkfirmware/zmk/blob/main/app/dts/bindings/zmk%2Ckeymap.yaml)
The `zmk,keymap` node itself has no properties. It should have one child node per layer of the keymap, starting with the default layer (layer 0).
The `zmk,keymap` node itself has no properties. It should have one child node per layer of the keymap, the first one being the default layer (until changed with [`&df`](layers.md#default-layer)).
Each child node can have the following properties: