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

@ -1,6 +1,12 @@
# Copyright (c) 2023 The ZMK Contributors # Copyright (c) 2023 The ZMK Contributors
# SPDX-License-Identifier: MIT # SPDX-License-Identifier: MIT
config ZMK_BEHAVIOR_METADATA
bool "Metadata"
help
Enabling this option adds APIs for documenting and fetching
metadata describing a behaviors name, and supported parameters.
config ZMK_BEHAVIOR_KEY_TOGGLE config ZMK_BEHAVIOR_KEY_TOGGLE
bool bool
default y default y

View file

@ -10,6 +10,7 @@
/omit-if-no-ref/ bl: bcklight { /omit-if-no-ref/ bl: bcklight {
compatible = "zmk,behavior-backlight"; compatible = "zmk,behavior-backlight";
#binding-cells = <2>; #binding-cells = <2>;
display-name = "Backlight";
}; };
}; };
}; };

View file

@ -9,6 +9,7 @@
/omit-if-no-ref/ bt: bluetooth { /omit-if-no-ref/ bt: bluetooth {
compatible = "zmk,behavior-bluetooth"; compatible = "zmk,behavior-bluetooth";
#binding-cells = <2>; #binding-cells = <2>;
display-name = "Bluetooth";
}; };
}; };
}; };

View file

@ -12,6 +12,7 @@
compatible = "zmk,behavior-caps-word"; compatible = "zmk,behavior-caps-word";
#binding-cells = <0>; #binding-cells = <0>;
continue-list = <UNDERSCORE BACKSPACE DELETE>; continue-list = <UNDERSCORE BACKSPACE DELETE>;
display-name = "Caps Word";
}; };
}; };
}; };

View file

@ -10,6 +10,7 @@
ext_power: extpower { ext_power: extpower {
compatible = "zmk,behavior-ext-power"; compatible = "zmk,behavior-ext-power";
#binding-cells = <1>; #binding-cells = <1>;
display-name = "External Power";
}; };
}; };
}; };

View file

@ -13,6 +13,7 @@
#binding-cells = <0>; #binding-cells = <0>;
bindings = <&kp ESC>, <&kp GRAVE>; bindings = <&kp ESC>, <&kp GRAVE>;
mods = <(MOD_LGUI|MOD_LSFT|MOD_RGUI|MOD_RSFT)>; mods = <(MOD_LGUI|MOD_LSFT|MOD_RGUI|MOD_RSFT)>;
display-name = "Grave/Escape";
}; };
}; };
}; };

View file

@ -10,6 +10,7 @@
/omit-if-no-ref/ cp: kp: key_press { /omit-if-no-ref/ cp: kp: key_press {
compatible = "zmk,behavior-key-press"; compatible = "zmk,behavior-key-press";
#binding-cells = <1>; #binding-cells = <1>;
display-name = "Key Press";
}; };
}; };
}; };

View file

@ -12,6 +12,7 @@
compatible = "zmk,behavior-key-repeat"; compatible = "zmk,behavior-key-repeat";
#binding-cells = <0>; #binding-cells = <0>;
usage-pages = <HID_USAGE_KEY>; usage-pages = <HID_USAGE_KEY>;
display-name = "Key Repeat";
}; };
}; };
}; };

View file

@ -9,6 +9,7 @@
/omit-if-no-ref/ kt: key_toggle { /omit-if-no-ref/ kt: key_toggle {
compatible = "zmk,behavior-key-toggle"; compatible = "zmk,behavior-key-toggle";
#binding-cells = <1>; #binding-cells = <1>;
display-name = "Key Toggle";
}; };
}; };
}; };

View file

@ -12,6 +12,7 @@
flavor = "tap-preferred"; flavor = "tap-preferred";
tapping-term-ms = <200>; tapping-term-ms = <200>;
bindings = <&mo>, <&kp>; bindings = <&mo>, <&kp>;
display-name = "Layer-Tap";
}; };
}; };
}; };

View file

@ -12,6 +12,7 @@
flavor = "hold-preferred"; flavor = "hold-preferred";
tapping-term-ms = <200>; tapping-term-ms = <200>;
bindings = <&kp>, <&kp>; bindings = <&kp>, <&kp>;
display-name = "Mod-Tap";
}; };
}; };
}; };

View file

@ -9,6 +9,7 @@
/omit-if-no-ref/ mo: momentary_layer { /omit-if-no-ref/ mo: momentary_layer {
compatible = "zmk,behavior-momentary-layer"; compatible = "zmk,behavior-momentary-layer";
#binding-cells = <1>; #binding-cells = <1>;
display-name = "Momentary Layer";
}; };
}; };
}; };

View file

@ -9,6 +9,7 @@
/omit-if-no-ref/ none: none { /omit-if-no-ref/ none: none {
compatible = "zmk,behavior-none"; compatible = "zmk,behavior-none";
#binding-cells = <0>; #binding-cells = <0>;
display-name = "None";
}; };
}; };
}; };

View file

@ -9,6 +9,7 @@
/omit-if-no-ref/ out: outputs { /omit-if-no-ref/ out: outputs {
compatible = "zmk,behavior-outputs"; compatible = "zmk,behavior-outputs";
#binding-cells = <1>; #binding-cells = <1>;
display-name = "Output Selection";
}; };
}; };
}; };

View file

@ -12,6 +12,7 @@
sys_reset: sysreset { sys_reset: sysreset {
compatible = "zmk,behavior-reset"; compatible = "zmk,behavior-reset";
#binding-cells = <0>; #binding-cells = <0>;
display-name = "Reset";
}; };
// Behavior can be invoked on peripherals, so name must be <= 8 characters. // Behavior can be invoked on peripherals, so name must be <= 8 characters.
@ -19,6 +20,7 @@
compatible = "zmk,behavior-reset"; compatible = "zmk,behavior-reset";
type = <RST_UF2>; type = <RST_UF2>;
#binding-cells = <0>; #binding-cells = <0>;
display-name = "Bootloader";
}; };
}; };
}; };

View file

@ -10,6 +10,7 @@
rgb_ug: rgb_ug { rgb_ug: rgb_ug {
compatible = "zmk,behavior-rgb-underglow"; compatible = "zmk,behavior-rgb-underglow";
#binding-cells = <2>; #binding-cells = <2>;
display-name = "Underglow";
}; };
}; };
}; };

View file

@ -12,6 +12,7 @@
release-after-ms = <1000>; release-after-ms = <1000>;
bindings = <&kp>; bindings = <&kp>;
ignore-modifiers; ignore-modifiers;
display-name = "Sticky Key";
}; };
/omit-if-no-ref/ sl: sticky_layer { /omit-if-no-ref/ sl: sticky_layer {
compatible = "zmk,behavior-sticky-key"; compatible = "zmk,behavior-sticky-key";
@ -19,6 +20,7 @@
release-after-ms = <1000>; release-after-ms = <1000>;
bindings = <&mo>; bindings = <&mo>;
quick-release; quick-release;
display-name = "Sticky Layer";
}; };
}; };

View file

@ -9,6 +9,7 @@
/omit-if-no-ref/ to: to_layer { /omit-if-no-ref/ to: to_layer {
compatible = "zmk,behavior-to-layer"; compatible = "zmk,behavior-to-layer";
#binding-cells = <1>; #binding-cells = <1>;
display-name = "To Layer";
}; };
}; };
}; };

View file

@ -9,6 +9,7 @@
/omit-if-no-ref/ tog: toggle_layer { /omit-if-no-ref/ tog: toggle_layer {
compatible = "zmk,behavior-toggle-layer"; compatible = "zmk,behavior-toggle-layer";
#binding-cells = <1>; #binding-cells = <1>;
display-name = "Toggle Layer";
}; };
}; };
}; };

View file

@ -9,6 +9,7 @@
/omit-if-no-ref/ trans: transparent { /omit-if-no-ref/ trans: transparent {
compatible = "zmk,behavior-transparent"; compatible = "zmk,behavior-transparent";
#binding-cells = <0>; #binding-cells = <0>;
display-name = "Transparent";
}; };
}; };
}; };

View file

@ -0,0 +1,6 @@
# Copyright (c) 2024 The ZMK Contributors
# SPDX-License-Identifier: MIT
properties:
display-name:
type: string

View file

@ -1,6 +1,8 @@
# Copyright (c) 2020 The ZMK Contributors # Copyright (c) 2020 The ZMK Contributors
# SPDX-License-Identifier: MIT # SPDX-License-Identifier: MIT
include: behavior-metadata.yaml
properties: properties:
label: label:
type: string type: string

View file

@ -1,6 +1,8 @@
# Copyright (c) 2020 The ZMK Contributors # Copyright (c) 2020 The ZMK Contributors
# SPDX-License-Identifier: MIT # SPDX-License-Identifier: MIT
include: behavior-metadata.yaml
properties: properties:
label: label:
type: string type: string

View file

@ -1,6 +1,8 @@
# Copyright (c) 2020 The ZMK Contributors # Copyright (c) 2020 The ZMK Contributors
# SPDX-License-Identifier: MIT # SPDX-License-Identifier: MIT
include: behavior-metadata.yaml
properties: properties:
label: label:
type: string type: string

View file

@ -23,6 +23,39 @@
* (Internal use only.) * (Internal use only.)
*/ */
struct behavior_parameter_value_metadata {
char *display_name;
union {
uint32_t value;
struct {
int32_t min;
int32_t max;
} range;
};
enum {
BEHAVIOR_PARAMETER_VALUE_TYPE_NIL = 0,
BEHAVIOR_PARAMETER_VALUE_TYPE_VALUE = 1,
BEHAVIOR_PARAMETER_VALUE_TYPE_RANGE = 2,
BEHAVIOR_PARAMETER_VALUE_TYPE_HID_USAGE = 3,
BEHAVIOR_PARAMETER_VALUE_TYPE_LAYER_INDEX = 4,
} type;
};
struct behavior_parameter_metadata_set {
size_t param1_values_len;
const struct behavior_parameter_value_metadata *param1_values;
size_t param2_values_len;
const struct behavior_parameter_value_metadata *param2_values;
};
struct behavior_parameter_metadata {
size_t sets_len;
const struct behavior_parameter_metadata_set *sets;
};
enum behavior_sensor_binding_process_mode { enum behavior_sensor_binding_process_mode {
BEHAVIOR_SENSOR_BINDING_PROCESS_MODE_TRIGGER, BEHAVIOR_SENSOR_BINDING_PROCESS_MODE_TRIGGER,
BEHAVIOR_SENSOR_BINDING_PROCESS_MODE_DISCARD, BEHAVIOR_SENSOR_BINDING_PROCESS_MODE_DISCARD,
@ -37,6 +70,10 @@ typedef int (*behavior_sensor_keymap_binding_accept_data_callback_t)(
struct zmk_behavior_binding *binding, struct zmk_behavior_binding_event event, struct zmk_behavior_binding *binding, struct zmk_behavior_binding_event event,
const struct zmk_sensor_config *sensor_config, size_t channel_data_size, const struct zmk_sensor_config *sensor_config, size_t channel_data_size,
const struct zmk_sensor_channel_data channel_data[channel_data_size]); const struct zmk_sensor_channel_data channel_data[channel_data_size]);
#if IS_ENABLED(CONFIG_ZMK_BEHAVIOR_METADATA)
typedef int (*behavior_get_parameter_metadata_t)(
const struct device *behavior, struct behavior_parameter_metadata *param_metadata);
#endif // IS_ENABLED(CONFIG_ZMK_BEHAVIOR_METADATA)
enum behavior_locality { enum behavior_locality {
BEHAVIOR_LOCALITY_CENTRAL, BEHAVIOR_LOCALITY_CENTRAL,
@ -51,23 +88,54 @@ __subsystem struct behavior_driver_api {
behavior_keymap_binding_callback_t binding_released; behavior_keymap_binding_callback_t binding_released;
behavior_sensor_keymap_binding_accept_data_callback_t sensor_binding_accept_data; behavior_sensor_keymap_binding_accept_data_callback_t sensor_binding_accept_data;
behavior_sensor_keymap_binding_process_callback_t sensor_binding_process; behavior_sensor_keymap_binding_process_callback_t sensor_binding_process;
#if IS_ENABLED(CONFIG_ZMK_BEHAVIOR_METADATA)
behavior_get_parameter_metadata_t get_parameter_metadata;
const struct behavior_parameter_metadata *parameter_metadata;
#endif // IS_ENABLED(CONFIG_ZMK_BEHAVIOR_METADATA)
}; };
/** /**
* @endcond * @endcond
*/ */
struct zmk_behavior_metadata {
#if IS_ENABLED(CONFIG_ZMK_BEHAVIOR_METADATA)
const char *display_name;
#endif // IS_ENABLED(CONFIG_ZMK_BEHAVIOR_METADATA)
};
struct zmk_behavior_ref { struct zmk_behavior_ref {
const struct device *device; const struct device *device;
const struct zmk_behavior_metadata metadata;
}; };
#define ZMK_BEHAVIOR_REF_DT_NAME(node_id) _CONCAT(zmk_behavior_, DEVICE_DT_NAME_GET(node_id))
#if IS_ENABLED(CONFIG_ZMK_BEHAVIOR_METADATA)
#define ZMK_BEHAVIOR_METADATA_INITIALIZER(node_id) \
{ .display_name = DT_PROP_OR(node_id, display_name, DEVICE_DT_NAME(node_id)), }
#else
#define ZMK_BEHAVIOR_METADATA_INITIALIZER(node_id) \
{}
#endif // IS_ENABLED(CONFIG_ZMK_BEHAVIOR_METADATA)
#define ZMK_BEHAVIOR_REF_INITIALIZER(node_id, _dev) \
{ .device = _dev, .metadata = ZMK_BEHAVIOR_METADATA_INITIALIZER(node_id), }
#define ZMK_BEHAVIOR_REF_DEFINE(name, node_id, _dev) \
static const STRUCT_SECTION_ITERABLE(zmk_behavior_ref, name) = \
ZMK_BEHAVIOR_REF_INITIALIZER(node_id, _dev)
#define ZMK_BEHAVIOR_REF_DT_DEFINE(node_id) \
ZMK_BEHAVIOR_REF_DEFINE(ZMK_BEHAVIOR_REF_DT_NAME(node_id), node_id, DEVICE_DT_GET(node_id))
/** /**
* Registers @p node_id as a behavior. * Registers @p node_id as a behavior.
*/ */
#define BEHAVIOR_DEFINE(node_id) \ #define BEHAVIOR_DEFINE(node_id) ZMK_BEHAVIOR_REF_DT_DEFINE(node_id)
static const STRUCT_SECTION_ITERABLE(zmk_behavior_ref, \
_CONCAT(zmk_behavior_, DEVICE_DT_NAME_GET(node_id))) = { \
.device = DEVICE_DT_GET(node_id), \
}
/** /**
* @brief Like DEVICE_DT_DEFINE(), but also registers the device as a behavior. * @brief Like DEVICE_DT_DEFINE(), but also registers the device as a behavior.
@ -89,6 +157,52 @@ struct zmk_behavior_ref {
DEVICE_DT_INST_DEFINE(inst, __VA_ARGS__); \ DEVICE_DT_INST_DEFINE(inst, __VA_ARGS__); \
BEHAVIOR_DEFINE(DT_DRV_INST(inst)) BEHAVIOR_DEFINE(DT_DRV_INST(inst))
/**
* @brief Validate a given behavior binding is valid, including parameter validation
* if the metadata feature is enablued.
*
* @param binding The behavior binding to validate.
*
* @retval 0 if the passed in binding is valid.
* @retval -ENODEV if the binding references a non-existant behavior.
* @retval -EINVAL if parameters are not valid for the behavior metadata.
*/
int zmk_behavior_validate_binding(const struct zmk_behavior_binding *binding);
#if IS_ENABLED(CONFIG_ZMK_BEHAVIOR_METADATA)
int zmk_behavior_get_empty_param_metadata(const struct device *dev,
struct behavior_parameter_metadata *metadata);
/**
* @brief Validate a given behavior parameters match the behavior metadata.
*
* @param metadata The behavior metadata to validate against
* @param param1 The first parameter value
* @param param2 The second parameter value
*
* @retval 0 if the passed in parameters are valid.
* @retval -ENODEV if metadata is NULL.
* @retval -EINVAL if parameters are not valid for the metadata.
*/
int zmk_behavior_check_params_match_metadata(const struct behavior_parameter_metadata *metadata,
uint32_t param1, uint32_t param2);
/**
* @brief Validate a given behavior parameter matches the behavior metadata parameter values.
*
* @param values The values to validate against
* @param values_len How many values to check
* @param param The value to check.
*
* @retval 0 if the passed in parameter is valid.
* @retval -ENODEV if values is NULL.
* @retval -EINVAL if parameter is not valid for the value metadata.
*/
int zmk_behavior_validate_param_values(const struct behavior_parameter_value_metadata *values,
size_t values_len, uint32_t param);
#endif // IS_ENABLED(CONFIG_ZMK_BEHAVIOR_METADATA)
/** /**
* Syscall wrapper for zmk_behavior_get_binding(). * Syscall wrapper for zmk_behavior_get_binding().
* *
@ -120,6 +234,40 @@ static inline int z_impl_behavior_keymap_binding_convert_central_state_dependent
return api->binding_convert_central_state_dependent_params(binding, event); return api->binding_convert_central_state_dependent_params(binding, event);
} }
#if IS_ENABLED(CONFIG_ZMK_BEHAVIOR_METADATA)
/**
* @brief Determine where the behavior should be run
* @param behavior Pointer to the device structure for the driver instance.
*
* @retval Zero if successful.
* @retval Negative errno code if failure.
*/
__syscall int behavior_get_parameter_metadata(const struct device *behavior,
struct behavior_parameter_metadata *param_metadata);
static inline int
z_impl_behavior_get_parameter_metadata(const struct device *behavior,
struct behavior_parameter_metadata *param_metadata) {
if (behavior == NULL || param_metadata == NULL) {
return -EINVAL;
}
const struct behavior_driver_api *api = (const struct behavior_driver_api *)behavior->api;
if (api->get_parameter_metadata) {
return api->get_parameter_metadata(behavior, param_metadata);
} else if (api->parameter_metadata) {
*param_metadata = *api->parameter_metadata;
} else {
return -ENODEV;
}
return 0;
}
#endif // IS_ENABLED(CONFIG_ZMK_BEHAVIOR_METADATA)
/** /**
* @brief Determine where the behavior should be run * @brief Determine where the behavior should be run
* @param behavior Pointer to the device structure for the driver instance. * @param behavior Pointer to the device structure for the driver instance.

View file

@ -12,7 +12,7 @@
#define ZMK_BEHAVIOR_TRANSPARENT 1 #define ZMK_BEHAVIOR_TRANSPARENT 1
struct zmk_behavior_binding { struct zmk_behavior_binding {
char *behavior_dev; const char *behavior_dev;
uint32_t param1; uint32_t param1;
uint32_t param2; uint32_t param2;
}; };

View file

@ -11,6 +11,8 @@
#include <drivers/behavior.h> #include <drivers/behavior.h>
#include <zmk/behavior.h> #include <zmk/behavior.h>
#include <zmk/hid.h>
#include <zmk/matrix.h>
#include <zephyr/logging/log.h> #include <zephyr/logging/log.h>
LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL); 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; 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) #if IS_ENABLED(CONFIG_LOG)
static int check_behavior_names(void) { static int check_behavior_names(void) {
// Behavior names must be unique, but we don't have a good way to enforce this // 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 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 behavior_backlight_init(const struct device *dev) { return 0; }
static int static int
@ -89,6 +165,9 @@ static const struct behavior_driver_api behavior_backlight_driver_api = {
.binding_pressed = on_keymap_binding_pressed, .binding_pressed = on_keymap_binding_pressed,
.binding_released = on_keymap_binding_released, .binding_released = on_keymap_binding_released,
.locality = BEHAVIOR_LOCALITY_GLOBAL, .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, 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 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, static int on_keymap_binding_pressed(struct zmk_behavior_binding *binding,
struct zmk_behavior_binding_event event) { struct zmk_behavior_binding_event event) {
switch (binding->param1) { 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 = { static const struct behavior_driver_api behavior_bt_driver_api = {
.binding_pressed = on_keymap_binding_pressed, .binding_pressed = on_keymap_binding_pressed,
.binding_released = on_keymap_binding_released, .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, 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[]; 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 // this data is specific for each hold-tap
struct active_hold_tap { struct active_hold_tap {
int32_t position; int32_t position;
@ -652,9 +658,52 @@ static int on_hold_tap_binding_released(struct zmk_behavior_binding *binding,
return ZMK_BEHAVIOR_OPAQUE; 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 = { static const struct behavior_driver_api behavior_hold_tap_driver_api = {
.binding_pressed = on_hold_tap_binding_pressed, .binding_pressed = on_hold_tap_binding_pressed,
.binding_released = on_hold_tap_binding_released, .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) { 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) \ #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), \ .tapping_term_ms = DT_INST_PROP(n, tapping_term_ms), \
.hold_behavior_dev = DEVICE_DT_NAME(DT_INST_PHANDLE_BY_IDX(n, bindings, 0)), \ .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)), \ .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 = DT_INST_PROP(n, hold_trigger_key_positions), \
.hold_trigger_key_positions_len = DT_INST_PROP_LEN(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, \ static struct behavior_hold_tap_data behavior_hold_tap_data_##n = {}; \
POST_KERNEL, CONFIG_KERNEL_INIT_PRIORITY_DEFAULT, \ BEHAVIOR_DT_INST_DEFINE(n, behavior_hold_tap_init, NULL, &behavior_hold_tap_data_##n, \
&behavior_hold_tap_driver_api); &behavior_hold_tap_config_##n, POST_KERNEL, \
CONFIG_KERNEL_INIT_PRIORITY_DEFAULT, &behavior_hold_tap_driver_api);
DT_INST_FOREACH_STATUS_OKAY(KP_INST) DT_INST_FOREACH_STATUS_OKAY(KP_INST)

View file

@ -16,6 +16,27 @@
LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL); 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 behavior_key_press_init(const struct device *dev) { return 0; };
static int on_keymap_binding_pressed(struct zmk_behavior_binding *binding, 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 = { 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) \ #define KP_INST(n) \
BEHAVIOR_DT_INST_DEFINE(n, behavior_key_press_init, NULL, NULL, NULL, POST_KERNEL, \ 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; 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 = { static const struct behavior_driver_api behavior_key_toggle_driver_api = {
.binding_pressed = on_keymap_binding_pressed, .binding_pressed = on_keymap_binding_pressed,
.binding_released = on_keymap_binding_released, .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) \ #define KT_INST(n) \

View file

@ -34,6 +34,10 @@ struct behavior_macro_trigger_state {
struct behavior_macro_state { struct behavior_macro_state {
struct behavior_macro_trigger_state release_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; uint32_t press_bindings_count;
}; };
@ -209,9 +213,100 @@ static int on_macro_binding_released(struct zmk_behavior_binding *binding,
return ZMK_BEHAVIOR_OPAQUE; 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 = { static const struct behavior_driver_api behavior_macro_driver_api = {
.binding_pressed = on_macro_binding_pressed, .binding_pressed = on_macro_binding_pressed,
.binding_released = on_macro_binding_released, .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) \ #define TRANSFORMED_BEHAVIORS(n) \

View file

@ -15,6 +15,27 @@
LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL); 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_config {};
struct behavior_mo_data {}; 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 = { 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 = {}; 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 = { static const struct behavior_driver_api behavior_none_driver_api = {
.binding_pressed = on_keymap_binding_pressed, .binding_pressed = on_keymap_binding_pressed,
.binding_released = on_keymap_binding_released, .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, 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 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, static int on_keymap_binding_pressed(struct zmk_behavior_binding *binding,
struct zmk_behavior_binding_event event) { struct zmk_behavior_binding_event event) {
switch (binding->param1) { 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 = { static const struct behavior_driver_api behavior_outputs_driver_api = {
.binding_pressed = on_keymap_binding_pressed, .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, 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 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 behavior_rgb_underglow_init(const struct device *dev) { return 0; }
static int static int
@ -147,6 +260,9 @@ static const struct behavior_driver_api behavior_rgb_underglow_driver_api = {
.binding_pressed = on_keymap_binding_pressed, .binding_pressed = on_keymap_binding_pressed,
.binding_released = on_keymap_binding_released, .binding_released = on_keymap_binding_released,
.locality = BEHAVIOR_LOCALITY_GLOBAL, .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, 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_pressed = on_keymap_binding_pressed,
.binding_released = on_keymap_binding_released, .binding_released = on_keymap_binding_released,
.locality = BEHAVIOR_LOCALITY_GLOBAL, .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) \ #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; 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 = { static const struct behavior_driver_api behavior_sticky_key_driver_api = {
.binding_pressed = on_sticky_key_binding_pressed, .binding_pressed = on_sticky_key_binding_pressed,
.binding_released = on_sticky_key_binding_released, .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); 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; static struct behavior_sticky_key_data behavior_sticky_key_data;
#define KP_INST(n) \ #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)), \ .behavior = ZMK_KEYMAP_EXTRACT_BINDING(0, DT_DRV_INST(n)), \
.release_after_ms = DT_INST_PROP(n, release_after_ms), \ .release_after_ms = DT_INST_PROP(n, release_after_ms), \
.quick_release = DT_INST_PROP(n, quick_release), \ .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 = { static const struct behavior_driver_api behavior_tap_dance_driver_api = {
.binding_pressed = on_tap_dance_binding_pressed, .binding_pressed = on_tap_dance_binding_pressed,
.binding_released = on_tap_dance_binding_released, .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); 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; 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 = { static const struct behavior_driver_api behavior_to_driver_api = {
.binding_pressed = to_keymap_binding_pressed, .binding_pressed = to_keymap_binding_pressed,
.binding_released = to_keymap_binding_released, .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, 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; 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 = { static const struct behavior_driver_api behavior_tog_driver_api = {
.binding_pressed = tog_keymap_binding_pressed, .binding_pressed = tog_keymap_binding_pressed,
.binding_released = tog_keymap_binding_released, .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 = {}; static const struct behavior_tog_config behavior_tog_config = {};