Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

feat: Refine behavior_binding_event, nested trans #2848

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

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions app/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ target_sources_ifdef(CONFIG_ZMK_EXT_POWER app PRIVATE src/ext_power_generic.c)
target_sources_ifdef(CONFIG_ZMK_GPIO_KEY_WAKEUP_TRIGGER app PRIVATE src/gpio_key_wakeup_trigger.c)
target_sources(app PRIVATE src/events/activity_state_changed.c)
target_sources(app PRIVATE src/events/position_state_changed.c)
target_sources(app PRIVATE src/events/behavior_binding_event.c)
target_sources(app PRIVATE src/events/sensor_event.c)
target_sources_ifdef(CONFIG_ZMK_WPM app PRIVATE src/events/wpm_state_changed.c)
target_sources_ifdef(CONFIG_USB_DEVICE_STACK app PRIVATE src/events/usb_conn_state_changed.c)
Expand Down
59 changes: 4 additions & 55 deletions app/include/drivers/behavior.h
Original file line number Diff line number Diff line change
Expand Up @@ -56,20 +56,8 @@ struct behavior_parameter_metadata {
const struct behavior_parameter_metadata_set *sets;
};

enum behavior_sensor_binding_process_mode {
BEHAVIOR_SENSOR_BINDING_PROCESS_MODE_TRIGGER,
BEHAVIOR_SENSOR_BINDING_PROCESS_MODE_DISCARD,
};

typedef int (*behavior_keymap_binding_callback_t)(struct zmk_behavior_binding *binding,
struct zmk_behavior_binding_event event);
typedef int (*behavior_sensor_keymap_binding_process_callback_t)(
struct zmk_behavior_binding *binding, struct zmk_behavior_binding_event event,
enum behavior_sensor_binding_process_mode mode);
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_metadata_t)(
const struct device *behavior, struct behavior_parameter_metadata *param_metadata);
Expand All @@ -86,8 +74,7 @@ __subsystem struct behavior_driver_api {
behavior_keymap_binding_callback_t binding_convert_central_state_dependent_params;
behavior_keymap_binding_callback_t binding_pressed;
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;
behavior_keymap_binding_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;
Expand Down Expand Up @@ -371,41 +358,6 @@ static inline int z_impl_behavior_keymap_binding_released(struct zmk_behavior_bi
return api->binding_released(binding, event);
}

/**
* @brief Handle the a sensor keymap binding processing any incoming data from the sensor
* @param binding Sensor keymap binding which was triggered.
* @param sensor Pointer to the sensor device structure for the sensor driver instance.
* @param virtual_key_position ZMK_KEYMAP_LEN + sensor number
* @param timestamp Time at which the binding was triggered.
*
* @retval 0 If successful.
* @retval Negative errno code if failure.
*/
__syscall int behavior_sensor_keymap_binding_accept_data(
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);

static inline int z_impl_behavior_sensor_keymap_binding_accept_data(
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) {
const struct device *dev = zmk_behavior_get_binding(binding->behavior_dev);

if (dev == NULL) {
return -EINVAL;
}

const struct behavior_driver_api *api = (const struct behavior_driver_api *)dev->api;

if (api->sensor_binding_accept_data == NULL) {
return -ENOTSUP;
}

return api->sensor_binding_accept_data(binding, event, sensor_config, channel_data_size,
channel_data);
}

/**
* @brief Handle the keymap sensor binding being triggered after updating any local data
* @param dev Pointer to the device structure for the driver instance.
Expand All @@ -418,14 +370,12 @@ static inline int z_impl_behavior_sensor_keymap_binding_accept_data(
// clang-format off
__syscall int behavior_sensor_keymap_binding_process(
struct zmk_behavior_binding *binding,
struct zmk_behavior_binding_event event,
enum behavior_sensor_binding_process_mode mode);
struct zmk_behavior_binding_event event);
// clang-format on

static inline int
z_impl_behavior_sensor_keymap_binding_process(struct zmk_behavior_binding *binding,
struct zmk_behavior_binding_event event,
enum behavior_sensor_binding_process_mode mode) {
struct zmk_behavior_binding_event event) {
const struct device *dev = zmk_behavior_get_binding(binding->behavior_dev);

if (dev == NULL) {
Expand All @@ -438,9 +388,8 @@ z_impl_behavior_sensor_keymap_binding_process(struct zmk_behavior_binding *bindi
return -ENOTSUP;
}

return api->sensor_binding_process(binding, event, mode);
return api->sensor_binding_process(binding, event);
}

/**
* @}
*/
Expand Down
27 changes: 5 additions & 22 deletions app/include/zmk/behavior.h
Original file line number Diff line number Diff line change
Expand Up @@ -8,29 +8,10 @@

#include <zephyr/device.h>

#define ZMK_BEHAVIOR_OPAQUE 0
#define ZMK_BEHAVIOR_TRANSPARENT 1

typedef uint16_t zmk_behavior_local_id_t;

struct zmk_behavior_binding {
#if IS_ENABLED(CONFIG_ZMK_BEHAVIOR_LOCAL_IDS_IN_BINDINGS)
zmk_behavior_local_id_t local_id;
#endif // IS_ENABLED(CONFIG_ZMK_BEHAVIOR_LOCAL_IDS_IN_BINDINGS)
const char *behavior_dev;
uint32_t param1;
uint32_t param2;
};

struct zmk_behavior_binding_event {
int layer;
uint32_t position;
int64_t timestamp;
#if IS_ENABLED(CONFIG_ZMK_SPLIT)
uint8_t source;
#endif
};
// TODO: Remove this

#define ZMK_BEHAVIOR_OPAQUE 0
#include <zmk/events/behavior_binding_event.h>
/**
* @brief Get a const struct device* for a behavior from its @p name field.
*
Expand All @@ -54,6 +35,8 @@ const struct device *zmk_behavior_get_binding(const char *name);
*
* @retval 0 If successful.
* @retval Negative errno code if failure.
*
* Deprecated. Raise the event directly instead.
*/
int zmk_behavior_invoke_binding(const struct zmk_behavior_binding *src_binding,
struct zmk_behavior_binding_event event, bool pressed);
Expand Down
35 changes: 35 additions & 0 deletions app/include/zmk/events/behavior_binding_event.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
/*
* Copyright (c) 2025 The ZMK Contributors
*
* SPDX-License-Identifier: MIT
*/

#pragma once

#include <zephyr/kernel.h>
#include <zmk/event_manager.h>
#include <zephyr/sys/util.h>

typedef uint16_t zmk_behavior_local_id_t;

struct zmk_behavior_binding {
#if IS_ENABLED(CONFIG_ZMK_BEHAVIOR_LOCAL_IDS_IN_BINDINGS)
zmk_behavior_local_id_t local_id;
#endif // IS_ENABLED(CONFIG_ZMK_BEHAVIOR_LOCAL_IDS_IN_BINDINGS)
const char *behavior_dev;
uint32_t param1;
uint32_t param2;
};

enum trigger_type { PRESS, RELEASE, SENSOR };

struct zmk_behavior_binding_event {
const struct zmk_behavior_binding *binding;
int layer;
uint32_t position;
int64_t timestamp;
enum trigger_type type;
uint8_t source;
};

ZMK_EVENT_DECLARE(zmk_behavior_binding_event);
6 changes: 4 additions & 2 deletions app/include/zmk/keymap.h
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
#pragma once

#include <zmk/events/position_state_changed.h>
#include <zmk/events/behavior_binding_event.h>

#define ZMK_LAYER_CHILD_LEN_PLUS_ONE(node) 1 +
#define ZMK_KEYMAP_LAYERS_LEN \
Expand Down Expand Up @@ -73,8 +74,9 @@ int zmk_keymap_save_changes(void);
int zmk_keymap_discard_changes(void);
int zmk_keymap_reset_settings(void);

int zmk_keymap_position_state_changed(uint8_t source, uint32_t position, bool pressed,
int64_t timestamp);
int zmk_keymap_raise_binding_event_at_layer_idx(zmk_keymap_layer_id_t layer_id, uint8_t source,
uint32_t position, enum trigger_type type,
int64_t timestamp);

#define ZMK_KEYMAP_EXTRACT_BINDING(idx, drv_inst) \
{ \
Expand Down
11 changes: 11 additions & 0 deletions app/include/zmk/sensors.h
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,20 @@ struct zmk_sensor_config {
uint16_t triggers_per_rotation;
};

struct zmk_sensor_data {
struct sensor_value remainder;
int num_triggers;
};

// This struct is also used for data transfer for splits, so any changes to the size, layout, etc
// is a breaking change for the split GATT service protocol.
struct zmk_sensor_channel_data {
struct sensor_value value;
enum sensor_channel channel;
} __packed;

struct zmk_sensor_data *zmk_sensor_get_data(uint32_t sensor_idx);

void zmk_sensor_set_num_triggers(uint32_t sensor_idx, int num_triggers);

void zmk_sensor_set_remainder(uint32_t sensor_idx, struct sensor_value remainder);
2 changes: 1 addition & 1 deletion app/include/zmk/split/bluetooth/central.h
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
#endif // IS_ENABLED(CONFIG_ZMK_SPLIT_PERIPHERAL_HID_INDICATORS)

int zmk_split_bt_invoke_behavior(uint8_t source, struct zmk_behavior_binding *binding,
struct zmk_behavior_binding_event event, bool state);
struct zmk_behavior_binding_event *event);

#if IS_ENABLED(CONFIG_ZMK_SPLIT_PERIPHERAL_HID_INDICATORS)

Expand Down
82 changes: 58 additions & 24 deletions app/src/behavior.c
Original file line number Diff line number Diff line change
Expand Up @@ -24,10 +24,12 @@

#include <drivers/behavior.h>
#include <zmk/behavior.h>
#include <zmk/event_manager.h>
#include <zmk/hid.h>
#include <zmk/matrix.h>

#include <zmk/events/position_state_changed.h>
#include <zmk/events/behavior_binding_event.h>

#include <zephyr/logging/log.h>
LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL);
Expand Down Expand Up @@ -56,29 +58,58 @@ const struct device *z_impl_behavior_get_binding(const char *name) {
return NULL;
}

// TODO: Delete this method as part of a breaking release
int zmk_behavior_invoke_binding(const struct zmk_behavior_binding *src_binding,
struct zmk_behavior_binding_event event, bool pressed) {
LOG_DBG("`zmk_behavior_invoke_binding` is deprecated. Please raise a "
"`zmk_behavior_binding_event` instead.");
return raise_zmk_behavior_binding_event((struct zmk_behavior_binding_event){
.binding = src_binding,
.layer = event.layer,
.position = event.position,
.timestamp = event.timestamp,
.type = pressed ? PRESS : RELEASE,
#if IS_ENABLED(CONFIG_ZMK_SPLIT)
.source = event.source,
#endif
});
}

// TODO: Pass the event in as pointers, rather than copying in the struct
// Make this change as part of a breaking release
static int invoke_locally(struct zmk_behavior_binding *binding,
struct zmk_behavior_binding_event event, bool pressed) {
if (pressed) {
return behavior_keymap_binding_pressed(binding, event);
} else {
return behavior_keymap_binding_released(binding, event);
struct zmk_behavior_binding_event *event) {
switch (event->type) {
case PRESS:
return behavior_keymap_binding_pressed(binding, *event);
case RELEASE:
return behavior_keymap_binding_released(binding, *event);
case SENSOR:
return behavior_sensor_keymap_binding_process(binding, *event);
default:
return -EINVAL;
}
}

int zmk_behavior_invoke_binding(const struct zmk_behavior_binding *src_binding,
struct zmk_behavior_binding_event event, bool pressed) {
// We want to make a copy of this, since it may be converted from
// relative to absolute before being invoked
struct zmk_behavior_binding binding = *src_binding;

const struct device *behavior = zmk_behavior_get_binding(binding.behavior_dev);
int behavior_listener(const zmk_event_t *eh) {
struct zmk_behavior_binding_event *event = as_zmk_behavior_binding_event(eh);
if (event == NULL) {
return -EINVAL;
}

const struct device *behavior = zmk_behavior_get_binding(event->binding->behavior_dev);
if (!behavior) {
LOG_WRN("No behavior assigned to %d on layer %d", event.position, event.layer);
return 1;
LOG_WRN("No behavior assigned to %d on layer %d", event->position, event->layer);
return 0;
}

int err = behavior_keymap_binding_convert_central_state_dependent_params(&binding, event);
/*
* Copying the behavior is necessary so that
* behavior_keymap_binding_convert_central_state_dependent_params (used for e.g. certain
* RGB behaviors) does not modify it, e.g. if the behavior is stored in a combo_cfg
*/
struct zmk_behavior_binding binding = *(event->binding);
int err = behavior_keymap_binding_convert_central_state_dependent_params(&binding, *event);
if (err) {
LOG_ERR("Failed to convert relative to absolute behavior binding (err %d)", err);
return err;
Expand All @@ -93,29 +124,32 @@ int zmk_behavior_invoke_binding(const struct zmk_behavior_binding *src_binding,

switch (locality) {
case BEHAVIOR_LOCALITY_CENTRAL:
return invoke_locally(&binding, event, pressed);
return invoke_locally(&binding, event);
case BEHAVIOR_LOCALITY_EVENT_SOURCE:
#if ZMK_BLE_IS_CENTRAL // source is a member of event because CONFIG_ZMK_SPLIT is enabled
if (event.source == ZMK_POSITION_STATE_CHANGE_SOURCE_LOCAL) {
return invoke_locally(&binding, event, pressed);
if (event->source == ZMK_POSITION_STATE_CHANGE_SOURCE_LOCAL) {
return invoke_locally(&binding, event);
} else {
return zmk_split_bt_invoke_behavior(event.source, &binding, event, pressed);
return zmk_split_bt_invoke_behavior(event->source, &binding, event);
}
#else
return invoke_locally(&binding, event, pressed);
return invoke_locally(&binding, event);
#endif
case BEHAVIOR_LOCALITY_GLOBAL:
#if ZMK_BLE_IS_CENTRAL
for (int i = 0; i < ZMK_SPLIT_BLE_PERIPHERAL_COUNT; i++) {
zmk_split_bt_invoke_behavior(i, &binding, event, pressed);
zmk_split_bt_invoke_behavior(i, &binding, event);
}
#endif
return invoke_locally(&binding, event, pressed);
return invoke_locally(&binding, event);
default:
return -ENOTSUP;
}

return -ENOTSUP;
}

ZMK_LISTENER(behavior, behavior_listener);
ZMK_SUBSCRIPTION(behavior, zmk_behavior_binding_event);

#if IS_ENABLED(CONFIG_ZMK_BEHAVIOR_METADATA)

int zmk_behavior_get_empty_param_metadata(const struct device *dev,
Expand Down
Loading