Skip to content

Commit

Permalink
feat(behaviors): Add behavior metadata information.
Browse files Browse the repository at this point in the history
* 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.
  • Loading branch information
petejohanson committed Apr 5, 2024
1 parent fe509c4 commit fda849e
Show file tree
Hide file tree
Showing 36 changed files with 592 additions and 7 deletions.
4 changes: 4 additions & 0 deletions app/Kconfig.behaviors
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
# Copyright (c) 2023 The ZMK Contributors
# SPDX-License-Identifier: MIT

config ZMK_BEHAVIOR_METADATA
bool "Metadata"
default y

config ZMK_BEHAVIOR_KEY_TOGGLE
bool
default y
Expand Down
1 change: 1 addition & 0 deletions app/dts/behaviors/backlight.dtsi
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
/omit-if-no-ref/ bl: bcklight {
compatible = "zmk,behavior-backlight";
#binding-cells = <2>;
friendly-name = "Backlight";
};
};
};
1 change: 1 addition & 0 deletions app/dts/behaviors/bluetooth.dtsi
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
/omit-if-no-ref/ bt: bluetooth {
compatible = "zmk,behavior-bluetooth";
#binding-cells = <2>;
friendly-name = "Bluetooth";
};
};
};
1 change: 1 addition & 0 deletions app/dts/behaviors/caps_word.dtsi
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
compatible = "zmk,behavior-caps-word";
#binding-cells = <0>;
continue-list = <UNDERSCORE BACKSPACE DELETE>;
friendly-name = "Caps Word";
};
};
};
Expand Down
1 change: 1 addition & 0 deletions app/dts/behaviors/ext_power.dtsi
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
ext_power: extpower {
compatible = "zmk,behavior-ext-power";
#binding-cells = <1>;
friendly-name = "External Power";
};
};
};
1 change: 1 addition & 0 deletions app/dts/behaviors/gresc.dtsi
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
#binding-cells = <0>;
bindings = <&kp ESC>, <&kp GRAVE>;
mods = <(MOD_LGUI|MOD_LSFT|MOD_RGUI|MOD_RSFT)>;
friendly-name = "Grave/Escape";
};
};
};
1 change: 1 addition & 0 deletions app/dts/behaviors/key_press.dtsi
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
/omit-if-no-ref/ cp: kp: key_press {
compatible = "zmk,behavior-key-press";
#binding-cells = <1>;
friendly-name = "Key Press";
};
};
};
1 change: 1 addition & 0 deletions app/dts/behaviors/key_repeat.dtsi
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
compatible = "zmk,behavior-key-repeat";
#binding-cells = <0>;
usage-pages = <HID_USAGE_KEY>;
friendly-name = "Key Repeat";
};
};
};
Expand Down
1 change: 1 addition & 0 deletions app/dts/behaviors/key_toggle.dtsi
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
/omit-if-no-ref/ kt: key_toggle {
compatible = "zmk,behavior-key-toggle";
#binding-cells = <1>;
friendly-name = "Key Toggle";
};
};
};
1 change: 1 addition & 0 deletions app/dts/behaviors/layer_tap.dtsi
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
flavor = "tap-preferred";
tapping-term-ms = <200>;
bindings = <&mo>, <&kp>;
friendly-name = "Layer-Tap";
};
};
};
1 change: 1 addition & 0 deletions app/dts/behaviors/mod_tap.dtsi
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
flavor = "hold-preferred";
tapping-term-ms = <200>;
bindings = <&kp>, <&kp>;
friendly-name = "Mod-Tap";
};
};
};
1 change: 1 addition & 0 deletions app/dts/behaviors/momentary_layer.dtsi
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
/omit-if-no-ref/ mo: momentary_layer {
compatible = "zmk,behavior-momentary-layer";
#binding-cells = <1>;
friendly-name = "Momentary Layer";
};
};
};
1 change: 1 addition & 0 deletions app/dts/behaviors/none.dtsi
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
/omit-if-no-ref/ none: none {
compatible = "zmk,behavior-none";
#binding-cells = <0>;
friendly-name = "None";
};
};
};
1 change: 1 addition & 0 deletions app/dts/behaviors/outputs.dtsi
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
/omit-if-no-ref/ out: outputs {
compatible = "zmk,behavior-outputs";
#binding-cells = <1>;
friendly-name = "Output Selection";
};
};
};
2 changes: 2 additions & 0 deletions app/dts/behaviors/reset.dtsi
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,15 @@
sys_reset: sysreset {
compatible = "zmk,behavior-reset";
#binding-cells = <0>;
friendly-name = "Reset";
};

// Behavior can be invoked on peripherals, so name must be <= 8 characters.
bootloader: bootload {
compatible = "zmk,behavior-reset";
type = <RST_UF2>;
#binding-cells = <0>;
friendly-name = "Bootloader";
};
};
};
1 change: 1 addition & 0 deletions app/dts/behaviors/rgb_underglow.dtsi
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
rgb_ug: rgb_ug {
compatible = "zmk,behavior-rgb-underglow";
#binding-cells = <2>;
friendly-name = "Underglow";
};
};
};
2 changes: 2 additions & 0 deletions app/dts/behaviors/sticky_key.dtsi
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,15 @@
release-after-ms = <1000>;
bindings = <&kp>;
ignore-modifiers;
friendly-name = "Sticky Key";
};
/omit-if-no-ref/ sl: sticky_layer {
compatible = "zmk,behavior-sticky-key";
#binding-cells = <1>;
release-after-ms = <1000>;
bindings = <&mo>;
quick-release;
friendly-name = "Sticky Layer";
};
};

Expand Down
1 change: 1 addition & 0 deletions app/dts/behaviors/to_layer.dtsi
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
/omit-if-no-ref/ to: to_layer {
compatible = "zmk,behavior-to-layer";
#binding-cells = <1>;
friendly-name = "To Layer";
};
};
};
1 change: 1 addition & 0 deletions app/dts/behaviors/toggle_layer.dtsi
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
/omit-if-no-ref/ tog: toggle_layer {
compatible = "zmk,behavior-toggle-layer";
#binding-cells = <1>;
friendly-name = "Toggle Layer";
};
};
};
1 change: 1 addition & 0 deletions app/dts/behaviors/transparent.dtsi
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
/omit-if-no-ref/ trans: transparent {
compatible = "zmk,behavior-transparent";
#binding-cells = <0>;
friendly-name = "Transparent";
};
};
};
6 changes: 6 additions & 0 deletions app/dts/bindings/behaviors/behavior-metadata.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
# Copyright (c) 2024 The ZMK Contributors
# SPDX-License-Identifier: MIT

properties:
friendly-name:
type: string
2 changes: 2 additions & 0 deletions app/dts/bindings/behaviors/one_param.yaml
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
# Copyright (c) 2020 The ZMK Contributors
# SPDX-License-Identifier: MIT

include: behavior-metadata.yaml

properties:
label:
type: string
Expand Down
2 changes: 2 additions & 0 deletions app/dts/bindings/behaviors/two_param.yaml
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
# Copyright (c) 2020 The ZMK Contributors
# SPDX-License-Identifier: MIT

include: behavior-metadata.yaml

properties:
label:
type: string
Expand Down
2 changes: 2 additions & 0 deletions app/dts/bindings/behaviors/zero_param.yaml
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
# Copyright (c) 2020 The ZMK Contributors
# SPDX-License-Identifier: MIT

include: behavior-metadata.yaml

properties:
label:
type: string
Expand Down
142 changes: 137 additions & 5 deletions app/include/drivers/behavior.h
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,64 @@
* (Internal use only.)
*/

#if IS_ENABLED(CONFIG_ZMK_BEHAVIOR_METADATA)

enum behavior_parameter_standard_domain {
BEHAVIOR_PARAMETER_STANDARD_DOMAIN_NULL = 0,
BEHAVIOR_PARAMETER_STANDARD_DOMAIN_HID_USAGE = 1,
BEHAVIOR_PARAMETER_STANDARD_DOMAIN_LAYER_INDEX = 2,
BEHAVIOR_PARAMETER_STANDARD_DOMAIN_HSV = 3,
};

struct behavior_parameter_value_metadata {
char *friendly_name;
uint8_t position;

union {
uint32_t value;
struct {
uint16_t min;
uint16_t max;
} range;

enum behavior_parameter_standard_domain standard;
};

enum {
BEHAVIOR_PARAMETER_VALUE_METADATA_TYPE_VALUE = 0,
BEHAVIOR_PARAMETER_VALUE_METADATA_TYPE_RANGE = 1,
BEHAVIOR_PARAMETER_VALUE_METADATA_TYPE_STANDARD = 2,
} type;
};

struct behavior_parameter_metadata_custom_set {
size_t values_len;
const struct behavior_parameter_value_metadata *values;
};

struct behavior_parameter_metadata_custom {
size_t sets_len;
struct behavior_parameter_metadata_custom_set sets[];
};

struct behavior_parameter_metadata {
union {
struct {
uint16_t param1;
uint16_t param2;
} standard;

const struct behavior_parameter_metadata_custom *custom;
};

enum {
BEHAVIOR_PARAMETER_METADATA_STANDARD = 0,
BEHAVIOR_PARAMETER_METADATA_CUSTOM = 1,
} type;
};

#endif // IS_ENABLED(CONFIG_ZMK_BEHAVIOR_METADATA)

enum behavior_sensor_binding_process_mode {
BEHAVIOR_SENSOR_BINDING_PROCESS_MODE_TRIGGER,
BEHAVIOR_SENSOR_BINDING_PROCESS_MODE_DISCARD,
Expand All @@ -37,6 +95,10 @@ typedef int (*behavior_sensor_keymap_binding_accept_data_callback_t)(
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_channel_data channel_data[channel_data_size]);
#if IS_ENABLED(CONFIG_ZMK_BEHAVIOR_METADATA)
typedef int (*behavior_get_parameter_domains_t)(const struct device *behavior,
struct behavior_parameter_metadata *param_metadata);
#endif // IS_ENABLED(CONFIG_ZMK_BEHAVIOR_METADATA)

enum behavior_locality {
BEHAVIOR_LOCALITY_CENTRAL,
Expand All @@ -51,23 +113,56 @@ __subsystem struct behavior_driver_api {
behavior_keymap_binding_callback_t binding_released;
behavior_sensor_keymap_binding_accept_data_callback_t sensor_binding_accept_data;
behavior_sensor_keymap_binding_process_callback_t sensor_binding_process;
#if IS_ENABLED(CONFIG_ZMK_BEHAVIOR_METADATA)
behavior_get_parameter_domains_t get_parameter_domains;
const struct behavior_parameter_metadata_custom *custom_parameters;
const enum behavior_parameter_standard_domain param1_standard_domain;
const enum behavior_parameter_standard_domain param2_standard_domain;
#endif // IS_ENABLED(CONFIG_ZMK_BEHAVIOR_METADATA)
};
/**
* @endcond
*/

struct zmk_behavior_metadata {
#if IS_ENABLED(CONFIG_ZMK_BEHAVIOR_METADATA)
const char *friendly_name;
#endif // IS_ENABLED(CONFIG_ZMK_BEHAVIOR_METADATA)
};

struct zmk_behavior_ref {
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) \
{ .friendly_name = DT_PROP_OR(node_id, friendly_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.
*/
#define BEHAVIOR_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), \
}
#define BEHAVIOR_DEFINE(node_id) ZMK_BEHAVIOR_REF_DT_DEFINE(node_id)

/**
* @brief Like DEVICE_DT_DEFINE(), but also registers the device as a behavior.
Expand Down Expand Up @@ -120,6 +215,43 @@ static inline int z_impl_behavior_keymap_binding_convert_central_state_dependent
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_domains(const struct device *behavior,
struct behavior_parameter_metadata *param_metadata);

static inline int
z_impl_behavior_get_parameter_domains(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_domains) {
return api->get_parameter_domains(behavior, param_metadata);
} else if (api->custom_parameters) {
param_metadata->type = BEHAVIOR_PARAMETER_METADATA_CUSTOM;
param_metadata->custom = api->custom_parameters;
} else {
param_metadata->type = BEHAVIOR_PARAMETER_METADATA_STANDARD;
param_metadata->standard.param1 = api->param1_standard_domain;
param_metadata->standard.param2 = api->param2_standard_domain;
}

return 0;
}

#endif // 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.
Expand Down
Loading

0 comments on commit fda849e

Please sign in to comment.