Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: default layer setter #2222

Open
wants to merge 7 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
feat: &df behavior and docs
  • Loading branch information
elpekenin committed Apr 4, 2024
commit 4c6818a86edc30c97ce3d22d9264c6d7ae29a044
1 change: 1 addition & 0 deletions app/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,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)
Expand Down
1 change: 1 addition & 0 deletions app/dts/behaviors.dtsi
Original file line number Diff line number Diff line change
Expand Up @@ -21,3 +21,4 @@
#include <behaviors/macros.dtsi>
#include <behaviors/mouse_key_press.dtsi>
#include <behaviors/soft_off.dtsi>
#include <behaviors/default_layer.dtsi>
14 changes: 14 additions & 0 deletions app/dts/behaviors/default_layer.dtsi
Original file line number Diff line number Diff line change
@@ -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>;
};
};
};
8 changes: 8 additions & 0 deletions app/dts/bindings/behaviors/zmk,behavior-default-layer.yaml
Original file line number Diff line number Diff line change
@@ -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
176 changes: 176 additions & 0 deletions app/src/behaviors/behavior_default_layer.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,176 @@
/*
* 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);

struct default_layer_settings_t {
uint8_t usb[ZMK_ENDPOINT_USB_COUNT];
uint8_t ble[ZMK_ENDPOINT_BLE_COUNT];
};
elpekenin marked this conversation as resolved.
Show resolved Hide resolved

static struct default_layer_settings_t default_layers = {0};

static int apply_default_layer_config(struct zmk_endpoint_instance endpoint) {
uint8_t layer = 0;

switch (endpoint.transport) {
case ZMK_TRANSPORT_USB:
__ASSERT(ZMK_ENDPOINT_USB_COUNT == 1, "Unreachable");
layer = default_layers.usb[0];
break;

case ZMK_TRANSPORT_BLE:
__ASSERT(endpoint.ble.profile_index < ZMK_ENDPOINT_BLE_COUNT, "Unreachable");
layer = default_layers.ble[endpoint.ble.profile_index];
break;
}

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;
}

settings_load_subtree("default_layer");

return apply_default_layer_config(zmk_endpoints_selected());
}
SYS_INIT(default_layer_init, APPLICATION, CONFIG_APPLICATION_INIT_PRIORITY);

#if DT_HAS_COMPAT_STATUS_OKAY(DT_DRV_COMPAT)

static int save_default_layer_setting(uint8_t layer, struct zmk_endpoint_instance endpoint) {
if (layer >= ZMK_KEYMAP_LAYERS_LEN) {
return -EINVAL;
}

switch (endpoint.transport) {
case ZMK_TRANSPORT_USB:
__ASSERT(ZMK_ENDPOINT_USB_COUNT == 1, "Unreachable");
default_layers.usb[0] = layer;
break;

case ZMK_TRANSPORT_BLE:
__ASSERT(endpoint.ble.profile_index < ZMK_ENDPOINT_BLE_COUNT, "Unreachable");
default_layers.ble[endpoint.ble.profile_index] = layer;
break;
}

int ret = settings_save_one("default_layer/settings", &default_layers, sizeof(default_layers));
elpekenin marked this conversation as resolved.
Show resolved Hide resolved
if (ret < 0) {
LOG_WRN("Could not update the settings.");
return ret;
}

if (endpoint.transport == ZMK_TRANSPORT_USB) {
LOG_INF("Updated default layer (%d) for USB endpoint.", layer);
} else {
LOG_INF("Updated default layer (%d) for BLE endpoint %d.", layer,
endpoint.ble.profile_index);
}
return 0;
}

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);
2 changes: 1 addition & 1 deletion app/src/keymap.c
Original file line number Diff line number Diff line change
Expand Up @@ -127,7 +127,7 @@ int zmk_keymap_layer_set_default(uint8_t layer) {
return ret;
}

LOG_DBG("default_layer_changed: %d", layer);
LOG_DBG("Default layer changed to: %d", layer);
return 0;
}

Expand Down
26 changes: 26 additions & 0 deletions docs/docs/behaviors/layers.md
Original file line number Diff line number Diff line change
Expand Up @@ -153,3 +153,29 @@ It is possible to use "toggle layer" to have keys that raise and lower the layer

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

The default layer 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.
elpekenin marked this conversation as resolved.
Show resolved Hide resolved

This 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
```
Loading