feat(behaviors): Add behavior metadata information.

* For upcoming ZMK studio work, make a set of rich metadata available
  to provide a friendly name for a behavior, and allow super flexible
  descriptions of the parameters the behaviors take.
* Add ability to validate a zmk_behavior_binding against
  the behavior metadata available.
This commit is contained in:
Peter Johanson
2024-03-27 12:27:49 -07:00
committed by Pete Johanson
parent 7cdf1e42ea
commit 03099b04b6
42 changed files with 965 additions and 14 deletions

View File

@@ -11,6 +11,8 @@
#include <drivers/behavior.h>
#include <zmk/behavior.h>
#include <zmk/hid.h>
#include <zmk/matrix.h>
#include <zephyr/logging/log.h>
LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL);
@@ -39,6 +41,150 @@ const struct device *z_impl_behavior_get_binding(const char *name) {
return NULL;
}
#if IS_ENABLED(CONFIG_ZMK_BEHAVIOR_METADATA)
int zmk_behavior_get_empty_param_metadata(const struct device *dev,
struct behavior_parameter_metadata *metadata) {
metadata->sets_len = 0;
return 0;
}
#endif // IS_ENABLED(CONFIG_ZMK_BEHAVIOR_METADATA)
#if IS_ENABLED(CONFIG_ZMK_BEHAVIOR_METADATA)
static int validate_hid_usage(uint16_t usage_page, uint16_t usage_id) {
LOG_DBG("Validate usage %d in page %d", usage_id, usage_page);
switch (usage_page) {
case HID_USAGE_KEY:
if (usage_id == 0 || (usage_id > ZMK_HID_KEYBOARD_NKRO_MAX_USAGE &&
usage_id < LEFT_CONTROL && usage_id > RIGHT_GUI)) {
return -EINVAL;
}
break;
case HID_USAGE_CONSUMER:
if (usage_id >
COND_CODE_1(IS_ENABLED(CONFIG_ZMK_HID_CONSUMER_REPORT_USAGES_BASIC), (0xFF), (0xFFF))) {
return -EINVAL;
}
break;
default:
LOG_WRN("Unsupported HID usage page %d", usage_page);
return -EINVAL;
}
return 0;
}
#define PARAM_MATCHES 0
static int check_param_matches_value(const struct behavior_parameter_value_metadata *value_meta,
uint32_t param) {
switch (value_meta->type) {
case BEHAVIOR_PARAMETER_VALUE_TYPE_NIL:
if (param == 0) {
return PARAM_MATCHES;
}
break;
case BEHAVIOR_PARAMETER_VALUE_TYPE_HID_USAGE:
if (validate_hid_usage(ZMK_HID_USAGE_PAGE(param), ZMK_HID_USAGE_ID(param)) >= 0) {
return PARAM_MATCHES;
}
break;
case BEHAVIOR_PARAMETER_VALUE_TYPE_LAYER_INDEX:
if (param >= 0 && param < ZMK_KEYMAP_LEN) {
return PARAM_MATCHES;
}
break;
/* TODO: Restore with HSV -> RGB refactor
case BEHAVIOR_PARAMETER_STANDARD_DOMAIN_HSV:
// TODO: No real way to validate? Maybe max brightness?
break;
*/
case BEHAVIOR_PARAMETER_VALUE_TYPE_VALUE:
if (param == value_meta->value) {
return PARAM_MATCHES;
}
break;
case BEHAVIOR_PARAMETER_VALUE_TYPE_RANGE:
if (param >= value_meta->range.min && param <= value_meta->range.max) {
return PARAM_MATCHES;
}
break;
default:
LOG_WRN("Unknown type %d", value_meta->type);
break;
}
return -ENOTSUP;
}
int zmk_behavior_validate_param_values(const struct behavior_parameter_value_metadata *values,
size_t values_len, uint32_t param) {
if (values_len == 0) {
return -ENODEV;
}
for (int v = 0; v < values_len; v++) {
int ret = check_param_matches_value(&values[v], param);
if (ret >= 0) {
return ret;
}
}
return -EINVAL;
}
int zmk_behavior_check_params_match_metadata(const struct behavior_parameter_metadata *metadata,
uint32_t param1, uint32_t param2) {
if (!metadata || metadata->sets_len == 0) {
if (!metadata) {
LOG_ERR("No metadata to check against");
}
return (param1 == 0 && param2 == 0) ? 0 : -ENODEV;
}
for (int s = 0; s < metadata->sets_len; s++) {
const struct behavior_parameter_metadata_set *set = &metadata->sets[s];
int param1_ret =
zmk_behavior_validate_param_values(set->param1_values, set->param1_values_len, param1);
int param2_ret =
zmk_behavior_validate_param_values(set->param2_values, set->param2_values_len, param2);
if ((param1_ret >= 0 || (param1_ret == -ENODEV && param1 == 0)) &&
(param2_ret >= 0 || (param2_ret == -ENODEV && param2 == 0))) {
return 0;
}
}
return -EINVAL;
}
#endif // IS_ENABLED(CONFIG_ZMK_BEHAVIOR_METADATA)
int zmk_behavior_validate_binding(const struct zmk_behavior_binding *binding) {
#if IS_ENABLED(CONFIG_ZMK_BEHAVIOR_METADATA)
const struct device *behavior = zmk_behavior_get_binding(binding->behavior_dev);
if (!behavior) {
return -ENODEV;
}
struct behavior_parameter_metadata metadata;
int ret = behavior_get_parameter_metadata(behavior, &metadata);
if (ret < 0) {
LOG_WRN("Failed getting metadata for %s: %d", binding->behavior_dev, ret);
return ret;
}
return zmk_behavior_check_params_match_metadata(&metadata, binding->param1, binding->param2);
#else
return 0;
#endif // IS_ENABLED(CONFIG_ZMK_BEHAVIOR_METADATA)
}
#if IS_ENABLED(CONFIG_LOG)
static int check_behavior_names(void) {
// Behavior names must be unique, but we don't have a good way to enforce this

View File

@@ -18,6 +18,82 @@ LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL);
#if DT_HAS_COMPAT_STATUS_OKAY(DT_DRV_COMPAT)
#if IS_ENABLED(CONFIG_ZMK_BEHAVIOR_METADATA)
static const struct behavior_parameter_value_metadata no_arg_values[] = {
{
.display_name = "Toggle On/Off",
.type = BEHAVIOR_PARAMETER_VALUE_TYPE_VALUE,
.value = BL_TOG_CMD,
},
{
.display_name = "Turn On",
.type = BEHAVIOR_PARAMETER_VALUE_TYPE_VALUE,
.value = BL_ON_CMD,
},
{
.display_name = "Turn OFF",
.type = BEHAVIOR_PARAMETER_VALUE_TYPE_VALUE,
.value = BL_OFF_CMD,
},
{
.display_name = "Increase Brightness",
.type = BEHAVIOR_PARAMETER_VALUE_TYPE_VALUE,
.value = BL_INC_CMD,
},
{
.display_name = "Decrease Brightness",
.type = BEHAVIOR_PARAMETER_VALUE_TYPE_VALUE,
.value = BL_DEC_CMD,
},
{
.display_name = "Cycle Brightness",
.type = BEHAVIOR_PARAMETER_VALUE_TYPE_VALUE,
.value = BL_CYCLE_CMD,
},
};
static const struct behavior_parameter_value_metadata one_arg_p1_values[] = {
{
.display_name = "Set Brightness",
.type = BEHAVIOR_PARAMETER_VALUE_TYPE_VALUE,
.value = BL_SET_CMD,
},
};
static const struct behavior_parameter_value_metadata one_arg_p2_values[] = {
{
.display_name = "Brightness",
.type = BEHAVIOR_PARAMETER_VALUE_TYPE_RANGE,
.range =
{
.min = 0,
.max = 255,
},
},
};
static const struct behavior_parameter_metadata_set no_args_set = {
.param1_values = no_arg_values,
.param1_values_len = ARRAY_SIZE(no_arg_values),
};
static const struct behavior_parameter_metadata_set one_args_set = {
.param1_values = one_arg_p1_values,
.param1_values_len = ARRAY_SIZE(one_arg_p1_values),
.param2_values = one_arg_p2_values,
.param2_values_len = ARRAY_SIZE(one_arg_p2_values),
};
static const struct behavior_parameter_metadata_set sets[] = {no_args_set, one_args_set};
static const struct behavior_parameter_metadata metadata = {
.sets_len = ARRAY_SIZE(sets),
.sets = sets,
};
#endif // IS_ENABLED(CONFIG_ZMK_BEHAVIOR_METADATA)
static int behavior_backlight_init(const struct device *dev) { return 0; }
static int
@@ -89,6 +165,9 @@ static const struct behavior_driver_api behavior_backlight_driver_api = {
.binding_pressed = on_keymap_binding_pressed,
.binding_released = on_keymap_binding_released,
.locality = BEHAVIOR_LOCALITY_GLOBAL,
#if IS_ENABLED(CONFIG_ZMK_BEHAVIOR_METADATA)
.parameter_metadata = &metadata,
#endif
};
BEHAVIOR_DT_INST_DEFINE(0, behavior_backlight_init, NULL, NULL, NULL, POST_KERNEL,

View File

@@ -20,6 +20,74 @@ LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL);
#if DT_HAS_COMPAT_STATUS_OKAY(DT_DRV_COMPAT)
#if IS_ENABLED(CONFIG_ZMK_BEHAVIOR_METADATA)
static const struct behavior_parameter_value_metadata no_arg_values[] = {
{
.display_name = "Next Profile",
.type = BEHAVIOR_PARAMETER_VALUE_TYPE_VALUE,
.value = BT_NXT_CMD,
},
{
.display_name = "Previous Profile",
.type = BEHAVIOR_PARAMETER_VALUE_TYPE_VALUE,
.value = BT_PRV_CMD,
},
{
.display_name = "Clear All Profiles",
.type = BEHAVIOR_PARAMETER_VALUE_TYPE_VALUE,
.value = BT_CLR_ALL_CMD,
},
{
.display_name = "Clear Selected Profile",
.type = BEHAVIOR_PARAMETER_VALUE_TYPE_VALUE,
.value = BT_CLR_CMD,
},
};
static const struct behavior_parameter_metadata_set no_args_set = {
.param1_values = no_arg_values,
.param1_values_len = ARRAY_SIZE(no_arg_values),
};
static const struct behavior_parameter_value_metadata prof_index_param1_values[] = {
{
.display_name = "Select Profile",
.type = BEHAVIOR_PARAMETER_VALUE_TYPE_VALUE,
.value = BT_SEL_CMD,
},
{
.display_name = "Disconnect Profile",
.type = BEHAVIOR_PARAMETER_VALUE_TYPE_VALUE,
.value = BT_DISC_CMD,
},
};
static const struct behavior_parameter_value_metadata prof_index_param2_values[] = {
{
.display_name = "Profile",
.type = BEHAVIOR_PARAMETER_VALUE_TYPE_RANGE,
.range = {.min = 0, .max = ZMK_BLE_PROFILE_COUNT},
},
};
static const struct behavior_parameter_metadata_set profile_index_metadata_set = {
.param1_values = prof_index_param1_values,
.param1_values_len = ARRAY_SIZE(prof_index_param1_values),
.param2_values = prof_index_param2_values,
.param2_values_len = ARRAY_SIZE(prof_index_param2_values),
};
static const struct behavior_parameter_metadata_set metadata_sets[] = {no_args_set,
profile_index_metadata_set};
static const struct behavior_parameter_metadata metadata = {
.sets_len = ARRAY_SIZE(metadata_sets),
.sets = metadata_sets,
};
#endif // IS_ENABLED(CONFIG_ZMK_BEHAVIOR_METADATA)
static int on_keymap_binding_pressed(struct zmk_behavior_binding *binding,
struct zmk_behavior_binding_event event) {
switch (binding->param1) {
@@ -54,6 +122,9 @@ static int on_keymap_binding_released(struct zmk_behavior_binding *binding,
static const struct behavior_driver_api behavior_bt_driver_api = {
.binding_pressed = on_keymap_binding_pressed,
.binding_released = on_keymap_binding_released,
#if IS_ENABLED(CONFIG_ZMK_BEHAVIOR_METADATA)
.parameter_metadata = &metadata,
#endif // IS_ENABLED(CONFIG_ZMK_BEHAVIOR_METADATA)
};
BEHAVIOR_DT_INST_DEFINE(0, behavior_bt_init, NULL, NULL, NULL, POST_KERNEL,

View File

@@ -68,6 +68,12 @@ struct behavior_hold_tap_config {
int32_t hold_trigger_key_positions[];
};
struct behavior_hold_tap_data {
#if IS_ENABLED(CONFIG_ZMK_BEHAVIOR_METADATA)
struct behavior_parameter_metadata_set set;
#endif // IS_ENABLED(CONFIG_ZMK_BEHAVIOR_METADATA)
};
// this data is specific for each hold-tap
struct active_hold_tap {
int32_t position;
@@ -652,9 +658,52 @@ static int on_hold_tap_binding_released(struct zmk_behavior_binding *binding,
return ZMK_BEHAVIOR_OPAQUE;
}
#if IS_ENABLED(CONFIG_ZMK_BEHAVIOR_METADATA)
static int hold_tap_parameter_metadata(const struct device *hold_tap,
struct behavior_parameter_metadata *param_metadata) {
const struct behavior_hold_tap_config *cfg = hold_tap->config;
struct behavior_hold_tap_data *data = hold_tap->data;
int err;
struct behavior_parameter_metadata child_meta;
err = behavior_get_parameter_metadata(zmk_behavior_get_binding(cfg->hold_behavior_dev),
&child_meta);
if (err < 0) {
LOG_WRN("Failed to get the hold behavior parameter: %d", err);
return err;
}
if (child_meta.sets_len > 0) {
data->set.param1_values = child_meta.sets[0].param1_values;
data->set.param1_values_len = child_meta.sets[0].param1_values_len;
}
err = behavior_get_parameter_metadata(zmk_behavior_get_binding(cfg->tap_behavior_dev),
&child_meta);
if (err < 0) {
LOG_WRN("Failed to get the tap behavior parameter: %d", err);
return err;
}
if (child_meta.sets_len > 0) {
data->set.param2_values = child_meta.sets[0].param1_values;
data->set.param2_values_len = child_meta.sets[0].param1_values_len;
}
param_metadata->sets = &data->set;
param_metadata->sets_len = 1;
return 0;
}
#endif // IS_ENABLED(CONFIG_ZMK_BEHAVIOR_METADATA)
static const struct behavior_driver_api behavior_hold_tap_driver_api = {
.binding_pressed = on_hold_tap_binding_pressed,
.binding_released = on_hold_tap_binding_released,
#if IS_ENABLED(CONFIG_ZMK_BEHAVIOR_METADATA)
.get_parameter_metadata = hold_tap_parameter_metadata,
#endif // IS_ENABLED(CONFIG_ZMK_BEHAVIOR_METADATA)
};
static int position_state_changed_listener(const zmk_event_t *eh) {
@@ -791,7 +840,7 @@ static int behavior_hold_tap_init(const struct device *dev) {
}
#define KP_INST(n) \
static struct behavior_hold_tap_config behavior_hold_tap_config_##n = { \
static const struct behavior_hold_tap_config behavior_hold_tap_config_##n = { \
.tapping_term_ms = DT_INST_PROP(n, tapping_term_ms), \
.hold_behavior_dev = DEVICE_DT_NAME(DT_INST_PHANDLE_BY_IDX(n, bindings, 0)), \
.tap_behavior_dev = DEVICE_DT_NAME(DT_INST_PHANDLE_BY_IDX(n, bindings, 1)), \
@@ -807,9 +856,10 @@ static int behavior_hold_tap_init(const struct device *dev) {
.hold_trigger_key_positions = DT_INST_PROP(n, hold_trigger_key_positions), \
.hold_trigger_key_positions_len = DT_INST_PROP_LEN(n, hold_trigger_key_positions), \
}; \
BEHAVIOR_DT_INST_DEFINE(n, behavior_hold_tap_init, NULL, NULL, &behavior_hold_tap_config_##n, \
POST_KERNEL, CONFIG_KERNEL_INIT_PRIORITY_DEFAULT, \
&behavior_hold_tap_driver_api);
static struct behavior_hold_tap_data behavior_hold_tap_data_##n = {}; \
BEHAVIOR_DT_INST_DEFINE(n, behavior_hold_tap_init, NULL, &behavior_hold_tap_data_##n, \
&behavior_hold_tap_config_##n, POST_KERNEL, \
CONFIG_KERNEL_INIT_PRIORITY_DEFAULT, &behavior_hold_tap_driver_api);
DT_INST_FOREACH_STATUS_OKAY(KP_INST)

View File

@@ -16,6 +16,27 @@
LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL);
#if IS_ENABLED(CONFIG_ZMK_BEHAVIOR_METADATA)
static const struct behavior_parameter_value_metadata param_values[] = {
{
.display_name = "Key",
.type = BEHAVIOR_PARAMETER_VALUE_TYPE_HID_USAGE,
},
};
static const struct behavior_parameter_metadata_set param_metadata_set[] = {{
.param1_values = param_values,
.param1_values_len = ARRAY_SIZE(param_values),
}};
static const struct behavior_parameter_metadata metadata = {
.sets_len = ARRAY_SIZE(param_metadata_set),
.sets = param_metadata_set,
};
#endif
static int behavior_key_press_init(const struct device *dev) { return 0; };
static int on_keymap_binding_pressed(struct zmk_behavior_binding *binding,
@@ -31,7 +52,12 @@ static int on_keymap_binding_released(struct zmk_behavior_binding *binding,
}
static const struct behavior_driver_api behavior_key_press_driver_api = {
.binding_pressed = on_keymap_binding_pressed, .binding_released = on_keymap_binding_released};
.binding_pressed = on_keymap_binding_pressed,
.binding_released = on_keymap_binding_released,
#if IS_ENABLED(CONFIG_ZMK_BEHAVIOR_METADATA)
.parameter_metadata = &metadata,
#endif // IS_ENABLED(CONFIG_ZMK_BEHAVIOR_METADATA)
};
#define KP_INST(n) \
BEHAVIOR_DT_INST_DEFINE(n, behavior_key_press_init, NULL, NULL, NULL, POST_KERNEL, \

View File

@@ -31,9 +31,34 @@ static int on_keymap_binding_released(struct zmk_behavior_binding *binding,
return 0;
}
#if IS_ENABLED(CONFIG_ZMK_BEHAVIOR_METADATA)
static const struct behavior_parameter_value_metadata param_values[] = {
{
.display_name = "Key",
.type = BEHAVIOR_PARAMETER_VALUE_TYPE_STANDARD,
.standard = BEHAVIOR_PARAMETER_STANDARD_DOMAIN_HID_USAGE,
},
};
static const struct behavior_parameter_metadata_set param_metadata_set[] = {{
.param1_values = param_values,
.param1_values_len = ARRAY_SIZE(param_values),
}};
static const struct behavior_parameter_metadata metadata = {
.sets_len = ARRAY_SIZE(param_metadata_set),
.sets = param_metadata_set,
};
#endif
static const struct behavior_driver_api behavior_key_toggle_driver_api = {
.binding_pressed = on_keymap_binding_pressed,
.binding_released = on_keymap_binding_released,
#if IS_ENABLED(CONFIG_ZMK_BEHAVIOR_METADATA)
.parameter_metadata = &metadata,
#endif // IS_ENABLED(CONFIG_ZMK_BEHAVIOR_METADATA)
};
#define KT_INST(n) \

View File

@@ -34,6 +34,10 @@ struct behavior_macro_trigger_state {
struct behavior_macro_state {
struct behavior_macro_trigger_state release_state;
#if IS_ENABLED(CONFIG_ZMK_BEHAVIOR_METADATA)
struct behavior_parameter_metadata_set set;
#endif // IS_ENABLED(CONFIG_ZMK_BEHAVIOR_METADATA)
uint32_t press_bindings_count;
};
@@ -209,9 +213,100 @@ static int on_macro_binding_released(struct zmk_behavior_binding *binding,
return ZMK_BEHAVIOR_OPAQUE;
}
#if IS_ENABLED(CONFIG_ZMK_BEHAVIOR_METADATA)
static void assign_values_to_set(enum param_source param_source,
struct behavior_parameter_metadata_set *set,
const struct behavior_parameter_value_metadata *values,
size_t values_len) {
if (param_source == PARAM_SOURCE_MACRO_1ST) {
set->param1_values = values;
set->param1_values_len = values_len;
} else {
set->param2_values = values;
set->param2_values_len = values_len;
}
}
// This function will dynamically determine the parameter metadata for a particular macro by
// inspecting the macro *bindings* to see what behaviors in that list receive the macro parameters,
// and then using the metadata from those behaviors for the macro itself.
//
// Care need be taken, where a behavior in the list takes two parameters, and the macro passes along
// a value for the *second* parameter, we need to make sure we find the right metadata set for the
// referenced behavior that matches the first parameter.
static int get_macro_parameter_metadata(const struct device *macro,
struct behavior_parameter_metadata *param_metadata) {
const struct behavior_macro_config *cfg = macro->config;
struct behavior_macro_state *data = macro->data;
struct behavior_macro_trigger_state state = {0};
for (int i = 0; (i < cfg->count) && (!data->set.param1_values || !data->set.param2_values);
i++) {
if (handle_control_binding(&state, &cfg->bindings[i]) ||
(state.param1_source == PARAM_SOURCE_BINDING &&
state.param2_source == PARAM_SOURCE_BINDING)) {
continue;
}
LOG_DBG("checking %d for the given state", i);
struct behavior_parameter_metadata binding_meta;
int err = behavior_get_parameter_metadata(
zmk_behavior_get_binding(cfg->bindings[i].behavior_dev), &binding_meta);
if (err < 0 || binding_meta.sets_len == 0) {
LOG_WRN("Failed to fetch macro binding parameter details %d", err);
return -ENOTSUP;
}
// If both macro parameters get passed to this one entry, use
// the metadata for this behavior verbatim.
if (state.param1_source != PARAM_SOURCE_BINDING &&
state.param2_source != PARAM_SOURCE_BINDING) {
param_metadata->sets_len = binding_meta.sets_len;
param_metadata->sets = binding_meta.sets;
return 0;
}
if (state.param1_source != PARAM_SOURCE_BINDING) {
assign_values_to_set(state.param1_source, &data->set,
binding_meta.sets[0].param1_values,
binding_meta.sets[0].param1_values_len);
}
if (state.param2_source != PARAM_SOURCE_BINDING) {
// For the param2 metadata, we need to find a set that matches fully bound first
// parameter of our macro entry, and use the metadata from that set.
for (int s = 0; s < binding_meta.sets_len; s++) {
if (zmk_behavior_validate_param_values(binding_meta.sets[s].param1_values,
binding_meta.sets[s].param1_values_len,
cfg->bindings[i].param1) >= 0) {
assign_values_to_set(state.param2_source, &data->set,
binding_meta.sets[s].param2_values,
binding_meta.sets[s].param2_values_len);
break;
}
}
}
state.param1_source = PARAM_SOURCE_BINDING;
state.param2_source = PARAM_SOURCE_BINDING;
}
param_metadata->sets_len = 1;
param_metadata->sets = &data->set;
return 0;
}
#endif // IS_ENABLED(CONFIG_ZMK_BEHAVIOR_METADATA)
static const struct behavior_driver_api behavior_macro_driver_api = {
.binding_pressed = on_macro_binding_pressed,
.binding_released = on_macro_binding_released,
#if IS_ENABLED(CONFIG_ZMK_BEHAVIOR_METADATA)
.get_parameter_metadata = get_macro_parameter_metadata,
#endif // IS_ENABLED(CONFIG_ZMK_BEHAVIOR_METADATA)
};
#define TRANSFORMED_BEHAVIORS(n) \

View File

@@ -15,6 +15,27 @@
LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL);
#if IS_ENABLED(CONFIG_ZMK_BEHAVIOR_METADATA)
static const struct behavior_parameter_value_metadata param_values[] = {
{
.display_name = "Layer",
.type = BEHAVIOR_PARAMETER_VALUE_TYPE_LAYER_INDEX,
},
};
static const struct behavior_parameter_metadata_set param_metadata_set[] = {{
.param1_values = param_values,
.param1_values_len = ARRAY_SIZE(param_values),
}};
static const struct behavior_parameter_metadata metadata = {
.sets_len = ARRAY_SIZE(param_metadata_set),
.sets = param_metadata_set,
};
#endif
struct behavior_mo_config {};
struct behavior_mo_data {};
@@ -33,7 +54,12 @@ static int mo_keymap_binding_released(struct zmk_behavior_binding *binding,
}
static const struct behavior_driver_api behavior_mo_driver_api = {
.binding_pressed = mo_keymap_binding_pressed, .binding_released = mo_keymap_binding_released};
.binding_pressed = mo_keymap_binding_pressed,
.binding_released = mo_keymap_binding_released,
#if IS_ENABLED(CONFIG_ZMK_BEHAVIOR_METADATA)
.parameter_metadata = &metadata,
#endif // IS_ENABLED(CONFIG_ZMK_BEHAVIOR_METADATA)
};
static const struct behavior_mo_config behavior_mo_config = {};

View File

@@ -31,6 +31,9 @@ static int on_keymap_binding_released(struct zmk_behavior_binding *binding,
static const struct behavior_driver_api behavior_none_driver_api = {
.binding_pressed = on_keymap_binding_pressed,
.binding_released = on_keymap_binding_released,
#if IS_ENABLED(CONFIG_ZMK_BEHAVIOR_METADATA)
.get_parameter_metadata = zmk_behavior_get_empty_param_metadata,
#endif // IS_ENABLED(CONFIG_ZMK_BEHAVIOR_METADATA)
};
BEHAVIOR_DT_INST_DEFINE(0, behavior_none_init, NULL, NULL, NULL, POST_KERNEL,

View File

@@ -20,6 +20,42 @@ LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL);
#if DT_HAS_COMPAT_STATUS_OKAY(DT_DRV_COMPAT)
#if IS_ENABLED(CONFIG_ZMK_BEHAVIOR_METADATA)
static const struct behavior_parameter_value_metadata std_values[] = {
{
.value = OUT_TOG,
.display_name = "Toggle Outputs",
.type = BEHAVIOR_PARAMETER_VALUE_TYPE_VALUE,
},
#if IS_ENABLED(CONFIG_ZMK_USB)
{
.value = OUT_USB,
.display_name = "USB Output",
.type = BEHAVIOR_PARAMETER_VALUE_TYPE_VALUE,
},
#endif // IS_ENABLED(CONFIG_ZMK_USB)
#if IS_ENABLED(CONFIG_ZMK_BLE)
{
.value = OUT_BLE,
.display_name = "BLE Output",
.type = BEHAVIOR_PARAMETER_VALUE_TYPE_VALUE,
},
#endif // IS_ENABLED(CONFIG_ZMK_BLE)
};
static const struct behavior_parameter_metadata_set std_set = {
.param1_values = std_values,
.param1_values_len = ARRAY_SIZE(std_values),
};
static const struct behavior_parameter_metadata metadata = {
.sets_len = 1,
.sets = &std_set,
};
#endif // IS_ENABLED(CONFIG_ZMK_BEHAVIOR_METADATA)
static int on_keymap_binding_pressed(struct zmk_behavior_binding *binding,
struct zmk_behavior_binding_event event) {
switch (binding->param1) {
@@ -40,6 +76,9 @@ static int behavior_out_init(const struct device *dev) { return 0; }
static const struct behavior_driver_api behavior_outputs_driver_api = {
.binding_pressed = on_keymap_binding_pressed,
#if IS_ENABLED(CONFIG_ZMK_BEHAVIOR_METADATA)
.parameter_metadata = &metadata,
#endif // IS_ENABLED(CONFIG_ZMK_BEHAVIOR_METADATA)
};
BEHAVIOR_DT_INST_DEFINE(0, behavior_out_init, NULL, NULL, NULL, POST_KERNEL,

View File

@@ -18,6 +18,119 @@ LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL);
#if DT_HAS_COMPAT_STATUS_OKAY(DT_DRV_COMPAT)
#if IS_ENABLED(CONFIG_ZMK_BEHAVIOR_METADATA)
static const struct behavior_parameter_value_metadata no_arg_values[] = {
{
.display_name = "Toggle On/Off",
.type = BEHAVIOR_PARAMETER_VALUE_TYPE_VALUE,
.value = RGB_TOG_CMD,
},
{
.display_name = "Turn On",
.type = BEHAVIOR_PARAMETER_VALUE_TYPE_VALUE,
.value = RGB_ON_CMD,
},
{
.display_name = "Turn OFF",
.type = BEHAVIOR_PARAMETER_VALUE_TYPE_VALUE,
.value = RGB_OFF_CMD,
},
{
.display_name = "Hue Up",
.type = BEHAVIOR_PARAMETER_VALUE_TYPE_VALUE,
.value = RGB_HUI_CMD,
},
{
.display_name = "Hue Down",
.type = BEHAVIOR_PARAMETER_VALUE_TYPE_VALUE,
.value = RGB_HUD_CMD,
},
{
.display_name = "Saturation Up",
.type = BEHAVIOR_PARAMETER_VALUE_TYPE_VALUE,
.value = RGB_SAI_CMD,
},
{
.display_name = "Saturation Down",
.type = BEHAVIOR_PARAMETER_VALUE_TYPE_VALUE,
.value = RGB_SAD_CMD,
},
{
.display_name = "Brightness Up",
.type = BEHAVIOR_PARAMETER_VALUE_TYPE_VALUE,
.value = RGB_BRI_CMD,
},
{
.display_name = "Brightness Down",
.type = BEHAVIOR_PARAMETER_VALUE_TYPE_VALUE,
.value = RGB_BRD_CMD,
},
{
.display_name = "Speed Up",
.type = BEHAVIOR_PARAMETER_VALUE_TYPE_VALUE,
.value = RGB_SPI_CMD,
},
{
.display_name = "Speed Down",
.type = BEHAVIOR_PARAMETER_VALUE_TYPE_VALUE,
.value = RGB_SPD_CMD,
},
{
.display_name = "Next Effect",
.type = BEHAVIOR_PARAMETER_VALUE_TYPE_VALUE,
.value = RGB_EFF_CMD,
},
{
.display_name = "Previous Effect",
.type = BEHAVIOR_PARAMETER_VALUE_TYPE_VALUE,
.value = RGB_EFR_CMD,
},
};
static const struct behavior_parameter_metadata_set no_args_set = {
.param1_values = no_arg_values,
.param1_values_len = ARRAY_SIZE(no_arg_values),
};
/*
static const struct behavior_parameter_value_metadata hsv_p1_value_metadata_values[] = {
{
.display_name = "Set Color",
.type = BEHAVIOR_PARAMETER_VALUE_TYPE_VALUE,
.value = RGB_COLOR_HSB_CMD,
},
};
static const struct behavior_parameter_value_metadata hsv_p2_value_metadata_values[] = {
{
.display_name = "Color",
.type = BEHAVIOR_PARAMETER_VALUE_TYPE_STANDARD,
.standard = BEHAVIOR_PARAMETER_STANDARD_DOMAIN_HSV,
},
};
static const struct behavior_parameter_metadata_set hsv_value_metadata_set = {
.param1_values = hsv_p1_value_metadata_values,
.param1_values_len = ARRAY_SIZE(hsv_p1_value_metadata_values),
.param_values = hsv_p2_value_metadata_values,
.param_values_len = ARRAY_SIZE(hsv_p2_value_metadata_values),
};
*/
static const struct behavior_parameter_metadata_set sets[] = {
no_args_set,
// hsv_value_metadata_set,
};
static const struct behavior_parameter_metadata metadata = {
.sets_len = ARRAY_SIZE(sets),
.sets = sets,
};
#endif // IS_ENABLED(CONFIG_ZMK_BEHAVIOR_METADATA)
static int behavior_rgb_underglow_init(const struct device *dev) { return 0; }
static int
@@ -147,6 +260,9 @@ static const struct behavior_driver_api behavior_rgb_underglow_driver_api = {
.binding_pressed = on_keymap_binding_pressed,
.binding_released = on_keymap_binding_released,
.locality = BEHAVIOR_LOCALITY_GLOBAL,
#if IS_ENABLED(CONFIG_ZMK_BEHAVIOR_METADATA)
.parameter_metadata = &metadata,
#endif
};
BEHAVIOR_DT_INST_DEFINE(0, behavior_rgb_underglow_init, NULL, NULL, NULL, POST_KERNEL,

View File

@@ -74,6 +74,9 @@ static const struct behavior_driver_api behavior_soft_off_driver_api = {
.binding_pressed = on_keymap_binding_pressed,
.binding_released = on_keymap_binding_released,
.locality = BEHAVIOR_LOCALITY_GLOBAL,
#if IS_ENABLED(CONFIG_ZMK_BEHAVIOR_METADATA)
.get_parameter_metadata = zmk_behavior_get_empty_param_metadata,
#endif // IS_ENABLED(CONFIG_ZMK_BEHAVIOR_METADATA)
};
#define BSO_INST(n) \

View File

@@ -188,9 +188,41 @@ static int on_sticky_key_binding_released(struct zmk_behavior_binding *binding,
return ZMK_BEHAVIOR_OPAQUE;
}
#if IS_ENABLED(CONFIG_ZMK_BEHAVIOR_METADATA)
static int sticky_key_parameter_domains(const struct device *sk,
struct behavior_parameter_metadata *param_metadata) {
const struct behavior_sticky_key_config *cfg = sk->config;
struct behavior_parameter_metadata child_metadata;
int err = behavior_get_parameter_metadata(zmk_behavior_get_binding(cfg->behavior.behavior_dev),
&child_metadata);
if (err < 0) {
LOG_WRN("Failed to get the sticky key bound behavior parameter: %d", err);
}
for (int s = 0; s < child_metadata.sets_len; s++) {
const struct behavior_parameter_metadata_set *set = &child_metadata.sets[s];
if (set->param2_values_len > 0) {
return -ENOTSUP;
}
}
*param_metadata = child_metadata;
return 0;
}
#endif // IS_ENABLED(CONFIG_ZMK_BEHAVIOR_METADATA)
static const struct behavior_driver_api behavior_sticky_key_driver_api = {
.binding_pressed = on_sticky_key_binding_pressed,
.binding_released = on_sticky_key_binding_released,
#if IS_ENABLED(CONFIG_ZMK_BEHAVIOR_METADATA)
.get_parameter_metadata = sticky_key_parameter_domains,
#endif // IS_ENABLED(CONFIG_ZMK_BEHAVIOR_METADATA)
};
static int sticky_key_keycode_state_changed_listener(const zmk_event_t *eh);
@@ -337,7 +369,7 @@ struct behavior_sticky_key_data {};
static struct behavior_sticky_key_data behavior_sticky_key_data;
#define KP_INST(n) \
static struct behavior_sticky_key_config behavior_sticky_key_config_##n = { \
static const struct behavior_sticky_key_config behavior_sticky_key_config_##n = { \
.behavior = ZMK_KEYMAP_EXTRACT_BINDING(0, DT_DRV_INST(n)), \
.release_after_ms = DT_INST_PROP(n, release_after_ms), \
.quick_release = DT_INST_PROP(n, quick_release), \

View File

@@ -189,6 +189,9 @@ void behavior_tap_dance_timer_handler(struct k_work *item) {
static const struct behavior_driver_api behavior_tap_dance_driver_api = {
.binding_pressed = on_tap_dance_binding_pressed,
.binding_released = on_tap_dance_binding_released,
#if IS_ENABLED(CONFIG_ZMK_BEHAVIOR_METADATA)
.get_parameter_metadata = zmk_behavior_get_empty_param_metadata,
#endif // IS_ENABLED(CONFIG_ZMK_BEHAVIOR_METADATA)
};
static int tap_dance_position_state_changed_listener(const zmk_event_t *eh);

View File

@@ -32,9 +32,34 @@ static int to_keymap_binding_released(struct zmk_behavior_binding *binding,
return ZMK_BEHAVIOR_OPAQUE;
}
#if IS_ENABLED(CONFIG_ZMK_BEHAVIOR_METADATA)
static const struct behavior_parameter_value_metadata param_values[] = {
{
.display_name = "Layer",
.type = BEHAVIOR_PARAMETER_VALUE_TYPE_STANDARD,
.standard = BEHAVIOR_PARAMETER_STANDARD_DOMAIN_LAYER_INDEX,
},
};
static const struct behavior_parameter_metadata_set param_metadata_set[] = {{
.param1_values = param_values,
.param1_values_len = ARRAY_SIZE(param_values),
}};
static const struct behavior_parameter_metadata metadata = {
.sets_len = ARRAY_SIZE(param_metadata_set),
.sets = param_metadata_set,
};
#endif
static const struct behavior_driver_api behavior_to_driver_api = {
.binding_pressed = to_keymap_binding_pressed,
.binding_released = to_keymap_binding_released,
#if IS_ENABLED(CONFIG_ZMK_BEHAVIOR_METADATA)
.parameter_metadata = &metadata,
#endif // IS_ENABLED(CONFIG_ZMK_BEHAVIOR_METADATA)
};
BEHAVIOR_DT_INST_DEFINE(0, behavior_to_init, NULL, NULL, NULL, POST_KERNEL,

View File

@@ -34,9 +34,34 @@ static int tog_keymap_binding_released(struct zmk_behavior_binding *binding,
return ZMK_BEHAVIOR_OPAQUE;
}
#if IS_ENABLED(CONFIG_ZMK_BEHAVIOR_METADATA)
static const struct behavior_parameter_value_metadata param_values[] = {
{
.display_name = "Layer",
.type = BEHAVIOR_PARAMETER_VALUE_TYPE_STANDARD,
.standard = BEHAVIOR_PARAMETER_STANDARD_DOMAIN_LAYER_INDEX,
},
};
static const struct behavior_parameter_metadata_set param_metadata_set[] = {{
.param1_values = param_values,
.param1_values_len = ARRAY_SIZE(param_values),
}};
static const struct behavior_parameter_metadata metadata = {
.sets_len = ARRAY_SIZE(param_metadata_set),
.sets = param_metadata_set,
};
#endif
static const struct behavior_driver_api behavior_tog_driver_api = {
.binding_pressed = tog_keymap_binding_pressed,
.binding_released = tog_keymap_binding_released,
#if IS_ENABLED(CONFIG_ZMK_BEHAVIOR_METADATA)
.parameter_metadata = &metadata,
#endif // IS_ENABLED(CONFIG_ZMK_BEHAVIOR_METADATA)
};
static const struct behavior_tog_config behavior_tog_config = {};