mirror of
https://github.com/zmkfirmware/zmk.git
synced 2026-03-20 04:55:20 -05:00
chore: Add basic tests for Studio's layer manipulation (#3164)
chore: Add test behaviors for Studio testing chore: Add basic tests for studio layer adjustment chore: Fixes from code review
This commit is contained in:
@@ -1,4 +1,5 @@
|
||||
zephyr_include_directories(include)
|
||||
|
||||
add_subdirectory(drivers)
|
||||
add_subdirectory(test-behaviors)
|
||||
add_subdirectory(lib)
|
||||
@@ -1,3 +1,4 @@
|
||||
|
||||
rsource "drivers/Kconfig"
|
||||
rsource "lib/Kconfig"
|
||||
rsource "lib/Kconfig"
|
||||
rsource "test-behaviors/Kconfig"
|
||||
@@ -0,0 +1,8 @@
|
||||
# Copyright (c) 2025 The ZMK Contributors
|
||||
# SPDX-License-Identifier: MIT
|
||||
|
||||
description: Layer Adder Behavior
|
||||
|
||||
compatible: "zmk,behavior-add-layer"
|
||||
|
||||
include: zero_param.yaml
|
||||
@@ -0,0 +1,8 @@
|
||||
# Copyright (c) 2025 The ZMK Contributors
|
||||
# SPDX-License-Identifier: MIT
|
||||
|
||||
description: Layer Mover Behavior
|
||||
|
||||
compatible: "zmk,behavior-move-layer"
|
||||
|
||||
include: two_param.yaml
|
||||
@@ -0,0 +1,8 @@
|
||||
# Copyright (c) 2025 The ZMK Contributors
|
||||
# SPDX-License-Identifier: MIT
|
||||
|
||||
description: Layer Remover Behavior
|
||||
|
||||
compatible: "zmk,behavior-remove-layer"
|
||||
|
||||
include: one_param.yaml
|
||||
@@ -0,0 +1,13 @@
|
||||
# Copyright (c) 2025 The ZMK Contributors
|
||||
# SPDX-License-Identifier: MIT
|
||||
|
||||
description: Layer Binding Setter Behavior
|
||||
|
||||
compatible: "zmk,behavior-set-layer-binding-at-idx"
|
||||
|
||||
include: two_param.yaml
|
||||
|
||||
properties:
|
||||
bindings:
|
||||
type: phandle-array
|
||||
required: true
|
||||
6
app/module/test-behaviors/CMakeLists.txt
Normal file
6
app/module/test-behaviors/CMakeLists.txt
Normal file
@@ -0,0 +1,6 @@
|
||||
if (((NOT CONFIG_ZMK_SPLIT) OR CONFIG_ZMK_SPLIT_ROLE_CENTRAL) AND CONFIG_ZMK_TEST_BEHAVIORS)
|
||||
target_sources(app PRIVATE behavior_add_layer.c)
|
||||
target_sources(app PRIVATE behavior_move_layer.c)
|
||||
target_sources(app PRIVATE behavior_remove_layer.c)
|
||||
target_sources(app PRIVATE behavior_set_layer_binding_at_idx.c)
|
||||
endif()
|
||||
2
app/module/test-behaviors/Kconfig
Normal file
2
app/module/test-behaviors/Kconfig
Normal file
@@ -0,0 +1,2 @@
|
||||
config ZMK_TEST_BEHAVIORS
|
||||
bool "Include behaviors used for testing purposes"
|
||||
50
app/module/test-behaviors/behavior_add_layer.c
Normal file
50
app/module/test-behaviors/behavior_add_layer.c
Normal file
@@ -0,0 +1,50 @@
|
||||
/*
|
||||
* Copyright (c) 2025 The ZMK Contributors
|
||||
*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
|
||||
#define DT_DRV_COMPAT zmk_behavior_add_layer
|
||||
|
||||
#include <zephyr/device.h>
|
||||
#include <drivers/behavior.h>
|
||||
#include <zephyr/logging/log.h>
|
||||
|
||||
#include <zmk/keymap.h>
|
||||
#include <zmk/behavior.h>
|
||||
|
||||
LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL);
|
||||
|
||||
#if (IS_ENABLED(CONFIG_ZMK_KEYMAP_LAYER_REORDERING)) && (DT_HAS_COMPAT_STATUS_OKAY(DT_DRV_COMPAT))
|
||||
|
||||
static int on_add_layer_binding_pressed(struct zmk_behavior_binding *binding,
|
||||
struct zmk_behavior_binding_event event) {
|
||||
int new_layer = zmk_keymap_add_layer();
|
||||
if (new_layer >= 0) {
|
||||
LOG_DBG("Added layer %d", new_layer);
|
||||
return 0;
|
||||
}
|
||||
switch (new_layer) {
|
||||
case -ENOSPC:
|
||||
LOG_ERR("No more layers can be added. Out of memory.");
|
||||
return -ENOSPC;
|
||||
default:
|
||||
LOG_ERR("Unknown error adding layer: %d", new_layer);
|
||||
return new_layer;
|
||||
}
|
||||
}
|
||||
|
||||
static int on_add_layer_binding_released(struct zmk_behavior_binding *binding,
|
||||
struct zmk_behavior_binding_event event) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct behavior_driver_api behavior_add_layer_driver_api = {
|
||||
.binding_pressed = on_add_layer_binding_pressed,
|
||||
.binding_released = on_add_layer_binding_released};
|
||||
|
||||
BEHAVIOR_DT_INST_DEFINE(0, NULL, NULL, NULL, NULL, POST_KERNEL, CONFIG_KERNEL_INIT_PRIORITY_DEFAULT,
|
||||
&behavior_add_layer_driver_api);
|
||||
|
||||
#endif // IS_ENABLED(CONFIG_ZMK_KEYMAP_LAYER_REORDERING) AND
|
||||
// DT_HAS_COMPAT_STATUS_OKAY(DT_DRV_COMPAT)
|
||||
47
app/module/test-behaviors/behavior_move_layer.c
Normal file
47
app/module/test-behaviors/behavior_move_layer.c
Normal file
@@ -0,0 +1,47 @@
|
||||
/*
|
||||
* Copyright (c) 2025 The ZMK Contributors
|
||||
*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
|
||||
#define DT_DRV_COMPAT zmk_behavior_move_layer
|
||||
|
||||
#include <zephyr/device.h>
|
||||
#include <drivers/behavior.h>
|
||||
#include <zephyr/logging/log.h>
|
||||
|
||||
#include <zmk/keymap.h>
|
||||
#include <zmk/behavior.h>
|
||||
|
||||
LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL);
|
||||
|
||||
#if (IS_ENABLED(CONFIG_ZMK_KEYMAP_LAYER_REORDERING)) && (DT_HAS_COMPAT_STATUS_OKAY(DT_DRV_COMPAT))
|
||||
|
||||
static int on_move_layer_binding_pressed(struct zmk_behavior_binding *binding,
|
||||
struct zmk_behavior_binding_event event) {
|
||||
int result = zmk_keymap_move_layer(binding->param1, binding->param2);
|
||||
|
||||
if (result < 0) {
|
||||
LOG_ERR("Failed to move layer from index %d to index %d (err: %d)", binding->param1,
|
||||
binding->param2, result);
|
||||
return result;
|
||||
}
|
||||
|
||||
LOG_DBG("Moved layer from index %d to index %d", binding->param1, binding->param2);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int on_move_layer_binding_released(struct zmk_behavior_binding *binding,
|
||||
struct zmk_behavior_binding_event event) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct behavior_driver_api behavior_move_layer_driver_api = {
|
||||
.binding_pressed = on_move_layer_binding_pressed,
|
||||
.binding_released = on_move_layer_binding_released};
|
||||
|
||||
BEHAVIOR_DT_INST_DEFINE(0, NULL, NULL, NULL, NULL, POST_KERNEL, CONFIG_KERNEL_INIT_PRIORITY_DEFAULT,
|
||||
&behavior_move_layer_driver_api);
|
||||
|
||||
#endif // IS_ENABLED(CONFIG_ZMK_KEYMAP_LAYER_REORDERING) AND
|
||||
// DT_HAS_COMPAT_STATUS_OKAY(DT_DRV_COMPAT)
|
||||
49
app/module/test-behaviors/behavior_remove_layer.c
Normal file
49
app/module/test-behaviors/behavior_remove_layer.c
Normal file
@@ -0,0 +1,49 @@
|
||||
/*
|
||||
* Copyright (c) 2025 The ZMK Contributors
|
||||
*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
|
||||
#define DT_DRV_COMPAT zmk_behavior_remove_layer
|
||||
|
||||
#include <zephyr/device.h>
|
||||
#include <drivers/behavior.h>
|
||||
#include <zephyr/logging/log.h>
|
||||
|
||||
#include <zmk/keymap.h>
|
||||
#include <zmk/behavior.h>
|
||||
|
||||
LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL);
|
||||
|
||||
#if (IS_ENABLED(CONFIG_ZMK_KEYMAP_LAYER_REORDERING)) && (DT_HAS_COMPAT_STATUS_OKAY(DT_DRV_COMPAT))
|
||||
|
||||
static int on_remove_layer_binding_pressed(struct zmk_behavior_binding *binding,
|
||||
struct zmk_behavior_binding_event event) {
|
||||
int result = zmk_keymap_remove_layer(binding->param1);
|
||||
if (result >= 0) {
|
||||
LOG_DBG("Removed layer at index %d", binding->param1);
|
||||
return 0;
|
||||
}
|
||||
switch (result) {
|
||||
case -EINVAL:
|
||||
LOG_ERR("Layer at index %d not found", binding->param1);
|
||||
return -EINVAL;
|
||||
default:
|
||||
LOG_DBG("Unknown error removing layer at index %d: %d", binding->param1, result);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
static int on_remove_layer_binding_released(struct zmk_behavior_binding *binding,
|
||||
struct zmk_behavior_binding_event event) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct behavior_driver_api behavior_remove_layer_driver_api = {
|
||||
.binding_pressed = on_remove_layer_binding_pressed,
|
||||
.binding_released = on_remove_layer_binding_released};
|
||||
|
||||
BEHAVIOR_DT_INST_DEFINE(0, NULL, NULL, NULL, NULL, POST_KERNEL, CONFIG_KERNEL_INIT_PRIORITY_DEFAULT,
|
||||
&behavior_remove_layer_driver_api);
|
||||
#endif // IS_ENABLED(CONFIG_ZMK_KEYMAP_LAYER_REORDERING) AND
|
||||
// DT_HAS_COMPAT_STATUS_OKAY(DT_DRV_COMPAT)
|
||||
100
app/module/test-behaviors/behavior_set_layer_binding_at_idx.c
Normal file
100
app/module/test-behaviors/behavior_set_layer_binding_at_idx.c
Normal file
@@ -0,0 +1,100 @@
|
||||
/*
|
||||
* Copyright (c) 2025 The ZMK Contributors
|
||||
*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
|
||||
#define DT_DRV_COMPAT zmk_behavior_set_layer_binding_at_idx
|
||||
|
||||
#include <zephyr/device.h>
|
||||
#include <drivers/behavior.h>
|
||||
#include <zephyr/logging/log.h>
|
||||
|
||||
#include <zmk/keymap.h>
|
||||
#include <zmk/behavior.h>
|
||||
|
||||
LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL);
|
||||
|
||||
#if DT_HAS_COMPAT_STATUS_OKAY(DT_DRV_COMPAT)
|
||||
|
||||
struct behavior_set_layer_binding_at_idx_config {
|
||||
size_t bindings_len;
|
||||
struct zmk_behavior_binding *bindings;
|
||||
};
|
||||
|
||||
struct behavior_set_layer_binding_at_idx_data {
|
||||
size_t current_idx;
|
||||
};
|
||||
|
||||
static int on_set_layer_binding_at_idx_binding_pressed(struct zmk_behavior_binding *binding,
|
||||
struct zmk_behavior_binding_event event) {
|
||||
const struct device *dev = zmk_behavior_get_binding(binding->behavior_dev);
|
||||
const struct behavior_set_layer_binding_at_idx_config *cfg = dev->config;
|
||||
struct behavior_set_layer_binding_at_idx_data *data = dev->data;
|
||||
|
||||
if (cfg->bindings_len == 0) {
|
||||
LOG_ERR("No bindings configured");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
struct zmk_behavior_binding *binding_to_set = &cfg->bindings[data->current_idx];
|
||||
|
||||
int result =
|
||||
zmk_keymap_set_layer_binding_at_idx(binding->param1, binding->param2, *binding_to_set);
|
||||
|
||||
if (result < 0) {
|
||||
LOG_ERR("Failed to set binding at layer %d, index %d (err: %d)", binding->param1,
|
||||
binding->param2, result);
|
||||
return result;
|
||||
}
|
||||
|
||||
LOG_DBG("Set binding at layer %d, index %d to binding %zu/%zu", binding->param1,
|
||||
binding->param2, data->current_idx + 1, cfg->bindings_len);
|
||||
|
||||
// Move to next binding, wrap around if at end
|
||||
data->current_idx = (data->current_idx + 1) % cfg->bindings_len;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int on_set_layer_binding_at_idx_binding_released(struct zmk_behavior_binding *binding,
|
||||
struct zmk_behavior_binding_event event) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct behavior_driver_api behavior_set_layer_binding_at_idx_driver_api = {
|
||||
.binding_pressed = on_set_layer_binding_at_idx_binding_pressed,
|
||||
.binding_released = on_set_layer_binding_at_idx_binding_released};
|
||||
|
||||
static int behavior_set_layer_binding_at_idx_init(const struct device *dev) {
|
||||
struct behavior_set_layer_binding_at_idx_data *data = dev->data;
|
||||
data->current_idx = 0;
|
||||
return 0;
|
||||
};
|
||||
|
||||
#define _TRANSFORM_ENTRY(idx, node) ZMK_KEYMAP_EXTRACT_BINDING(idx, node)
|
||||
|
||||
#define TRANSFORMED_BINDINGS(node) \
|
||||
{LISTIFY(DT_INST_PROP_LEN(node, bindings), _TRANSFORM_ENTRY, (, ), DT_DRV_INST(node))}
|
||||
|
||||
#define SET_LAYER_BINDING_AT_IDX_INST(n) \
|
||||
static struct behavior_set_layer_binding_at_idx_data \
|
||||
behavior_set_layer_binding_at_idx_data_##n = {}; \
|
||||
\
|
||||
static const struct zmk_behavior_binding \
|
||||
behavior_set_layer_binding_at_idx_config_##n##_bindings[DT_INST_PROP_LEN(n, bindings)] = \
|
||||
TRANSFORMED_BINDINGS(n); \
|
||||
static const struct behavior_set_layer_binding_at_idx_config \
|
||||
behavior_set_layer_binding_at_idx_config_##n = { \
|
||||
.bindings_len = DT_INST_PROP_LEN(n, bindings), \
|
||||
.bindings = behavior_set_layer_binding_at_idx_config_##n##_bindings}; \
|
||||
\
|
||||
BEHAVIOR_DT_INST_DEFINE(n, behavior_set_layer_binding_at_idx_init, NULL, \
|
||||
&behavior_set_layer_binding_at_idx_data_##n, \
|
||||
&behavior_set_layer_binding_at_idx_config_##n, POST_KERNEL, \
|
||||
CONFIG_KERNEL_INIT_PRIORITY_DEFAULT, \
|
||||
&behavior_set_layer_binding_at_idx_driver_api);
|
||||
|
||||
DT_INST_FOREACH_STATUS_OKAY(SET_LAYER_BINDING_AT_IDX_INST)
|
||||
|
||||
#endif // DT_HAS_COMPAT_STATUS_OKAY(DT_DRV_COMPAT)
|
||||
Reference in New Issue
Block a user