feat: Refactor behaviors, sensor to raise behavior_binding_event

This commit is contained in:
Nicolas Munnich 2025-03-19 00:11:33 +01:00 committed by Nicolas Munnich
parent ac7f75b859
commit d88dbd252c
30 changed files with 400 additions and 352 deletions

View File

@ -38,6 +38,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_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/activity_state_changed.c)
target_sources(app PRIVATE src/events/position_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(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_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) target_sources_ifdef(CONFIG_USB_DEVICE_STACK app PRIVATE src/events/usb_conn_state_changed.c)

View File

@ -56,20 +56,8 @@ struct behavior_parameter_metadata {
const struct behavior_parameter_metadata_set *sets; 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, typedef int (*behavior_keymap_binding_callback_t)(struct zmk_behavior_binding *binding,
struct zmk_behavior_binding_event event); 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) #if IS_ENABLED(CONFIG_ZMK_BEHAVIOR_METADATA)
typedef int (*behavior_get_parameter_metadata_t)( typedef int (*behavior_get_parameter_metadata_t)(
const struct device *behavior, struct behavior_parameter_metadata *param_metadata); const struct device *behavior, struct behavior_parameter_metadata *param_metadata);
@ -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_convert_central_state_dependent_params;
behavior_keymap_binding_callback_t binding_pressed; behavior_keymap_binding_callback_t binding_pressed;
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_keymap_binding_callback_t sensor_binding_process;
behavior_sensor_keymap_binding_process_callback_t sensor_binding_process;
#if IS_ENABLED(CONFIG_ZMK_BEHAVIOR_METADATA) #if IS_ENABLED(CONFIG_ZMK_BEHAVIOR_METADATA)
behavior_get_parameter_metadata_t get_parameter_metadata; behavior_get_parameter_metadata_t get_parameter_metadata;
const struct behavior_parameter_metadata *parameter_metadata; const struct behavior_parameter_metadata *parameter_metadata;
@ -371,41 +358,6 @@ static inline int z_impl_behavior_keymap_binding_released(struct zmk_behavior_bi
return api->binding_released(binding, event); 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 * @brief Handle the keymap sensor binding being triggered after updating any local data
* @param dev Pointer to the device structure for the driver instance. * @param dev Pointer to the device structure for the driver instance.
@ -418,14 +370,12 @@ static inline int z_impl_behavior_sensor_keymap_binding_accept_data(
// clang-format off // clang-format off
__syscall int behavior_sensor_keymap_binding_process( __syscall int behavior_sensor_keymap_binding_process(
struct zmk_behavior_binding *binding, struct zmk_behavior_binding *binding,
struct zmk_behavior_binding_event event, struct zmk_behavior_binding_event event);
enum behavior_sensor_binding_process_mode mode);
// clang-format on // clang-format on
static inline int static inline int
z_impl_behavior_sensor_keymap_binding_process(struct zmk_behavior_binding *binding, z_impl_behavior_sensor_keymap_binding_process(struct zmk_behavior_binding *binding,
struct zmk_behavior_binding_event event, struct zmk_behavior_binding_event event) {
enum behavior_sensor_binding_process_mode mode) {
const struct device *dev = zmk_behavior_get_binding(binding->behavior_dev); const struct device *dev = zmk_behavior_get_binding(binding->behavior_dev);
if (dev == NULL) { if (dev == NULL) {
@ -438,9 +388,8 @@ z_impl_behavior_sensor_keymap_binding_process(struct zmk_behavior_binding *bindi
return -ENOTSUP; return -ENOTSUP;
} }
return api->sensor_binding_process(binding, event, mode); return api->sensor_binding_process(binding, event);
} }
/** /**
* @} * @}
*/ */

View File

@ -8,29 +8,10 @@
#include <zephyr/device.h> #include <zephyr/device.h>
// TODO: Remove this
#define ZMK_BEHAVIOR_OPAQUE 0 #define ZMK_BEHAVIOR_OPAQUE 0
#define ZMK_BEHAVIOR_TRANSPARENT 1 #include <zmk/events/behavior_binding_event.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;
};
struct zmk_behavior_binding_event {
int layer;
uint32_t position;
int64_t timestamp;
#if IS_ENABLED(CONFIG_ZMK_SPLIT)
uint8_t source;
#endif
};
/** /**
* @brief Get a const struct device* for a behavior from its @p name field. * @brief Get a const struct device* for a behavior from its @p name field.
* *
@ -54,6 +35,8 @@ const struct device *zmk_behavior_get_binding(const char *name);
* *
* @retval 0 If successful. * @retval 0 If successful.
* @retval Negative errno code if failure. * @retval Negative errno code if failure.
*
* Deprecated. Raise the event directly instead.
*/ */
int zmk_behavior_invoke_binding(const struct zmk_behavior_binding *src_binding, int zmk_behavior_invoke_binding(const struct zmk_behavior_binding *src_binding,
struct zmk_behavior_binding_event event, bool pressed); struct zmk_behavior_binding_event event, bool pressed);

View File

@ -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);

View File

@ -7,6 +7,7 @@
#pragma once #pragma once
#include <zmk/events/position_state_changed.h> #include <zmk/events/position_state_changed.h>
#include <zmk/events/behavior_binding_event.h>
#include <zephyr/sys/util.h> #include <zephyr/sys/util.h>
#include <zephyr/devicetree.h> #include <zephyr/devicetree.h>
@ -68,6 +69,13 @@ int zmk_keymap_restore_layer(zmk_keymap_layer_id_t id, zmk_keymap_layer_index_t
int zmk_keymap_move_layer(zmk_keymap_layer_index_t start_idx, zmk_keymap_layer_index_t dest_idx); int zmk_keymap_move_layer(zmk_keymap_layer_index_t start_idx, zmk_keymap_layer_index_t dest_idx);
int zmk_keymap_set_layer_name(zmk_keymap_layer_id_t id, const char *name, size_t size); int zmk_keymap_set_layer_name(zmk_keymap_layer_id_t id, const char *name, size_t size);
uint8_t zmk_keymap_map_layer_id_to_index(zmk_keymap_layer_id_t layer_id);
#define LAYER_ID_TO_INDEX(_layer) zmk_keymap_map_layer_id_to_index(_layer)
#else
#define LAYER_ID_TO_INDEX(_layer) _layer
#endif #endif
@ -83,8 +91,9 @@ int zmk_keymap_save_changes(void);
int zmk_keymap_discard_changes(void); int zmk_keymap_discard_changes(void);
int zmk_keymap_reset_settings(void); int zmk_keymap_reset_settings(void);
int zmk_keymap_position_state_changed(uint8_t source, uint32_t position, bool pressed, int zmk_keymap_raise_binding_event_at_layer_index(zmk_keymap_layer_id_t layer_index, uint8_t source,
int64_t timestamp); uint32_t position, enum trigger_type type,
int64_t timestamp);
#define ZMK_KEYMAP_EXTRACT_BINDING(idx, drv_inst) \ #define ZMK_KEYMAP_EXTRACT_BINDING(idx, drv_inst) \
{ \ { \

View File

@ -24,9 +24,20 @@ struct zmk_sensor_config {
uint16_t triggers_per_rotation; 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 // 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. // is a breaking change for the split GATT service protocol.
struct zmk_sensor_channel_data { struct zmk_sensor_channel_data {
struct sensor_value value; struct sensor_value value;
enum sensor_channel channel; enum sensor_channel channel;
} __packed; } __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);

View File

@ -33,7 +33,7 @@
#endif // IS_ENABLED(CONFIG_ZMK_SPLIT_PERIPHERAL_HID_INDICATORS) #endif // IS_ENABLED(CONFIG_ZMK_SPLIT_PERIPHERAL_HID_INDICATORS)
int zmk_split_central_invoke_behavior(uint8_t source, struct zmk_behavior_binding *binding, int zmk_split_central_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) #if IS_ENABLED(CONFIG_ZMK_SPLIT_PERIPHERAL_HID_INDICATORS)

View File

@ -23,10 +23,12 @@
#include <drivers/behavior.h> #include <drivers/behavior.h>
#include <zmk/behavior.h> #include <zmk/behavior.h>
#include <zmk/event_manager.h>
#include <zmk/hid.h> #include <zmk/hid.h>
#include <zmk/matrix.h> #include <zmk/matrix.h>
#include <zmk/events/position_state_changed.h> #include <zmk/events/position_state_changed.h>
#include <zmk/events/behavior_binding_event.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);
@ -55,29 +57,59 @@ const struct device *z_impl_behavior_get_binding(const char *name) {
return NULL; 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, static int invoke_locally(struct zmk_behavior_binding *binding,
struct zmk_behavior_binding_event event, bool pressed) { struct zmk_behavior_binding_event *event) {
if (pressed) { switch (event->type) {
return behavior_keymap_binding_pressed(binding, event); case PRESS:
} else { return behavior_keymap_binding_pressed(binding, *event);
return behavior_keymap_binding_released(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, int behavior_listener(const zmk_event_t *eh) {
struct zmk_behavior_binding_event event, bool pressed) { struct zmk_behavior_binding_event *event = as_zmk_behavior_binding_event(eh);
// We want to make a copy of this, since it may be converted from if (event == NULL) {
// relative to absolute before being invoked LOG_ERR("An invalid event was passed as an argument somehow.");
struct zmk_behavior_binding binding = *src_binding; return -EINVAL;
const struct device *behavior = zmk_behavior_get_binding(binding.behavior_dev);
if (!behavior) {
LOG_WRN("No behavior assigned to %d on layer %d", event.position, event.layer);
return 1;
} }
int err = behavior_keymap_binding_convert_central_state_dependent_params(&binding, event); 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 0;
}
/*
* 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) { if (err) {
LOG_ERR("Failed to convert relative to absolute behavior binding (err %d)", err); LOG_ERR("Failed to convert relative to absolute behavior binding (err %d)", err);
return err; return err;
@ -92,29 +124,31 @@ int zmk_behavior_invoke_binding(const struct zmk_behavior_binding *src_binding,
switch (locality) { switch (locality) {
case BEHAVIOR_LOCALITY_CENTRAL: case BEHAVIOR_LOCALITY_CENTRAL:
return invoke_locally(&binding, event, pressed); return invoke_locally(&binding, event);
case BEHAVIOR_LOCALITY_EVENT_SOURCE: case BEHAVIOR_LOCALITY_EVENT_SOURCE:
#if IS_ENABLED(CONFIG_ZMK_SPLIT) && IS_ENABLED(CONFIG_ZMK_SPLIT_ROLE_CENTRAL) #if IS_ENABLED(CONFIG_ZMK_SPLIT) && IS_ENABLED(CONFIG_ZMK_SPLIT_ROLE_CENTRAL)
if (event.source == ZMK_POSITION_STATE_CHANGE_SOURCE_LOCAL) { if (event->source == ZMK_POSITION_STATE_CHANGE_SOURCE_LOCAL) {
return invoke_locally(&binding, event, pressed); return invoke_locally(&binding, event);
} else {
return zmk_split_central_invoke_behavior(event.source, &binding, event, pressed);
} }
return zmk_split_central_invoke_behavior(event->source, &binding, event);
#else #else
return invoke_locally(&binding, event, pressed); return invoke_locally(&binding, event);
#endif #endif
case BEHAVIOR_LOCALITY_GLOBAL: case BEHAVIOR_LOCALITY_GLOBAL:
#if IS_ENABLED(CONFIG_ZMK_SPLIT) && IS_ENABLED(CONFIG_ZMK_SPLIT_ROLE_CENTRAL) #if IS_ENABLED(CONFIG_ZMK_SPLIT) && IS_ENABLED(CONFIG_ZMK_SPLIT_ROLE_CENTRAL)
for (int i = 0; i < ZMK_SPLIT_CENTRAL_PERIPHERAL_COUNT; i++) { for (int i = 0; i < ZMK_SPLIT_CENTRAL_PERIPHERAL_COUNT; i++) {
zmk_split_central_invoke_behavior(i, &binding, event, pressed); zmk_split_central_invoke_behavior(i, &binding, event);
} }
#endif #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) #if IS_ENABLED(CONFIG_ZMK_BEHAVIOR_METADATA)
int zmk_behavior_get_empty_param_metadata(const struct device *dev, int zmk_behavior_get_empty_param_metadata(const struct device *dev,

View File

@ -30,24 +30,24 @@ static K_WORK_DELAYABLE_DEFINE(queue_work, behavior_queue_process_next);
static void behavior_queue_process_next(struct k_work *work) { static void behavior_queue_process_next(struct k_work *work) {
struct q_item item = {.wait = 0}; struct q_item item = {.wait = 0};
int ret;
while (k_msgq_get(&zmk_behavior_queue_msgq, &item, K_NO_WAIT) == 0) { while (k_msgq_get(&zmk_behavior_queue_msgq, &item, K_NO_WAIT) == 0) {
LOG_DBG("Invoking %s: 0x%02x 0x%02x", item.binding.behavior_dev, item.binding.param1, LOG_DBG("Invoking %s: 0x%02x 0x%02x", item.binding.behavior_dev, item.binding.param1,
item.binding.param2); item.binding.param2);
struct zmk_behavior_binding_event event = {.position = item.position, struct zmk_behavior_binding_event event = {.binding = &item.binding,
.position = item.position,
.timestamp = k_uptime_get(), .timestamp = k_uptime_get(),
.type = item.press ? PRESS : RELEASE,
#if IS_ENABLED(CONFIG_ZMK_SPLIT) #if IS_ENABLED(CONFIG_ZMK_SPLIT)
.source = item.source .source = item.source
#endif #endif
}; };
ret = raise_zmk_behavior_binding_event(event);
if (item.press) { if (ret < 0) {
zmk_behavior_invoke_binding(&item.binding, event, true); LOG_ERR("Error %d occurred while processing behavior in queue.", ret);
} else {
zmk_behavior_invoke_binding(&item.binding, event, false);
} }
LOG_DBG("Processing next queued behavior in %dms", item.wait); LOG_DBG("Processing next queued behavior in %dms", item.wait);
if (item.wait > 0) { if (item.wait > 0) {

View File

@ -76,6 +76,7 @@ struct behavior_hold_tap_data {
// 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;
uint8_t layer;
#if IS_ENABLED(CONFIG_ZMK_SPLIT) #if IS_ENABLED(CONFIG_ZMK_SPLIT)
uint8_t source; uint8_t source;
#endif #endif
@ -260,6 +261,7 @@ static struct active_hold_tap *store_hold_tap(struct zmk_behavior_binding_event
continue; continue;
} }
active_hold_taps[i].position = event->position; active_hold_taps[i].position = event->position;
active_hold_taps[i].layer = event->layer;
#if IS_ENABLED(CONFIG_ZMK_SPLIT) #if IS_ENABLED(CONFIG_ZMK_SPLIT)
active_hold_taps[i].source = event->source; active_hold_taps[i].source = event->source;
#endif #endif
@ -402,60 +404,72 @@ static inline const char *decision_moment_str(enum decision_moment decision_mome
} }
static int press_hold_binding(struct active_hold_tap *hold_tap) { static int press_hold_binding(struct active_hold_tap *hold_tap) {
struct zmk_behavior_binding binding = {.behavior_dev = hold_tap->config->hold_behavior_dev,
.param1 = hold_tap->param_hold};
struct zmk_behavior_binding_event event = { struct zmk_behavior_binding_event event = {
.binding = &binding,
.position = hold_tap->position, .position = hold_tap->position,
.timestamp = hold_tap->timestamp, .timestamp = hold_tap->timestamp,
.layer = hold_tap->layer,
.type = PRESS,
#if IS_ENABLED(CONFIG_ZMK_SPLIT) #if IS_ENABLED(CONFIG_ZMK_SPLIT)
.source = hold_tap->source, .source = hold_tap->source,
#endif #endif
}; };
struct zmk_behavior_binding binding = {.behavior_dev = hold_tap->config->hold_behavior_dev, return raise_zmk_behavior_binding_event(event);
.param1 = hold_tap->param_hold};
return zmk_behavior_invoke_binding(&binding, event, true);
} }
static int press_tap_binding(struct active_hold_tap *hold_tap) { static int press_tap_binding(struct active_hold_tap *hold_tap) {
struct zmk_behavior_binding binding = {.behavior_dev = hold_tap->config->tap_behavior_dev,
.param1 = hold_tap->param_tap};
struct zmk_behavior_binding_event event = { struct zmk_behavior_binding_event event = {
.binding = &binding,
.position = hold_tap->position, .position = hold_tap->position,
.timestamp = hold_tap->timestamp, .timestamp = hold_tap->timestamp,
.layer = hold_tap->layer,
.type = PRESS,
#if IS_ENABLED(CONFIG_ZMK_SPLIT) #if IS_ENABLED(CONFIG_ZMK_SPLIT)
.source = hold_tap->source, .source = hold_tap->source,
#endif #endif
}; };
LOG_DBG("tapping on layer%d", hold_tap->layer);
struct zmk_behavior_binding binding = {.behavior_dev = hold_tap->config->tap_behavior_dev,
.param1 = hold_tap->param_tap};
store_last_hold_tapped(hold_tap); store_last_hold_tapped(hold_tap);
return zmk_behavior_invoke_binding(&binding, event, true); return raise_zmk_behavior_binding_event(event);
} }
static int release_hold_binding(struct active_hold_tap *hold_tap) { static int release_hold_binding(struct active_hold_tap *hold_tap) {
struct zmk_behavior_binding binding = {.behavior_dev = hold_tap->config->hold_behavior_dev,
.param1 = hold_tap->param_hold};
struct zmk_behavior_binding_event event = { struct zmk_behavior_binding_event event = {
.binding = &binding,
.position = hold_tap->position, .position = hold_tap->position,
.timestamp = hold_tap->timestamp, .timestamp = hold_tap->timestamp,
.layer = hold_tap->layer,
.type = RELEASE,
#if IS_ENABLED(CONFIG_ZMK_SPLIT) #if IS_ENABLED(CONFIG_ZMK_SPLIT)
.source = hold_tap->source, .source = hold_tap->source,
#endif #endif
}; };
struct zmk_behavior_binding binding = {.behavior_dev = hold_tap->config->hold_behavior_dev, return raise_zmk_behavior_binding_event(event);
.param1 = hold_tap->param_hold};
return zmk_behavior_invoke_binding(&binding, event, false);
} }
static int release_tap_binding(struct active_hold_tap *hold_tap) { static int release_tap_binding(struct active_hold_tap *hold_tap) {
struct zmk_behavior_binding binding = {.behavior_dev = hold_tap->config->tap_behavior_dev,
.param1 = hold_tap->param_tap};
struct zmk_behavior_binding_event event = { struct zmk_behavior_binding_event event = {
.binding = &binding,
.position = hold_tap->position, .position = hold_tap->position,
.timestamp = hold_tap->timestamp, .timestamp = hold_tap->timestamp,
.layer = hold_tap->layer,
.type = RELEASE,
#if IS_ENABLED(CONFIG_ZMK_SPLIT) #if IS_ENABLED(CONFIG_ZMK_SPLIT)
.source = hold_tap->source, .source = hold_tap->source,
#endif #endif
}; };
struct zmk_behavior_binding binding = {.behavior_dev = hold_tap->config->tap_behavior_dev, return raise_zmk_behavior_binding_event(event);
.param1 = hold_tap->param_tap};
return zmk_behavior_invoke_binding(&binding, event, false);
} }
static int press_binding(struct active_hold_tap *hold_tap) { static int press_binding(struct active_hold_tap *hold_tap) {

View File

@ -51,7 +51,8 @@ static int on_mod_morph_binding_pressed(struct zmk_behavior_binding *binding,
} else { } else {
data->pressed_binding = (struct zmk_behavior_binding *)&cfg->normal_binding; data->pressed_binding = (struct zmk_behavior_binding *)&cfg->normal_binding;
} }
return zmk_behavior_invoke_binding(data->pressed_binding, event, true); event.binding = data->pressed_binding;
return raise_zmk_behavior_binding_event(event);
} }
static int on_mod_morph_binding_released(struct zmk_behavior_binding *binding, static int on_mod_morph_binding_released(struct zmk_behavior_binding *binding,
@ -67,7 +68,8 @@ static int on_mod_morph_binding_released(struct zmk_behavior_binding *binding,
struct zmk_behavior_binding *pressed_binding = data->pressed_binding; struct zmk_behavior_binding *pressed_binding = data->pressed_binding;
data->pressed_binding = NULL; data->pressed_binding = NULL;
int err; int err;
err = zmk_behavior_invoke_binding(pressed_binding, event, false); event.binding = pressed_binding;
err = raise_zmk_behavior_binding_event(event);
zmk_hid_masked_modifiers_clear(); zmk_hid_masked_modifiers_clear();
return err; return err;
} }

View File

@ -13,7 +13,6 @@
#include "behavior_sensor_rotate_common.h" #include "behavior_sensor_rotate_common.h"
static const struct behavior_driver_api behavior_sensor_rotate_driver_api = { static const struct behavior_driver_api behavior_sensor_rotate_driver_api = {
.sensor_binding_accept_data = zmk_behavior_sensor_rotate_common_accept_data,
.sensor_binding_process = zmk_behavior_sensor_rotate_common_process}; .sensor_binding_process = zmk_behavior_sensor_rotate_common_process};
#define _TRANSFORM_ENTRY(idx, node) \ #define _TRANSFORM_ENTRY(idx, node) \
@ -32,9 +31,8 @@ static const struct behavior_driver_api behavior_sensor_rotate_driver_api = {
.tap_ms = DT_INST_PROP_OR(n, tap_ms, 5), \ .tap_ms = DT_INST_PROP_OR(n, tap_ms, 5), \
.override_params = false, \ .override_params = false, \
}; \ }; \
static struct behavior_sensor_rotate_data behavior_sensor_rotate_data_##n = {}; \ BEHAVIOR_DT_INST_DEFINE(n, NULL, NULL, NULL, &behavior_sensor_rotate_config_##n, POST_KERNEL, \
BEHAVIOR_DT_INST_DEFINE( \ CONFIG_KERNEL_INIT_PRIORITY_DEFAULT, \
n, NULL, NULL, &behavior_sensor_rotate_data_##n, &behavior_sensor_rotate_config_##n, \ &behavior_sensor_rotate_driver_api);
POST_KERNEL, CONFIG_KERNEL_INIT_PRIORITY_DEFAULT, &behavior_sensor_rotate_driver_api);
DT_INST_FOREACH_STATUS_OKAY(SENSOR_ROTATE_INST) DT_INST_FOREACH_STATUS_OKAY(SENSOR_ROTATE_INST)

View File

@ -4,73 +4,23 @@
#include <zephyr/logging/log.h> #include <zephyr/logging/log.h>
#include <zephyr/kernel.h> #include <zephyr/kernel.h>
#include <zmk/sensors.h>
#include <zmk/behavior_queue.h> #include <zmk/behavior_queue.h>
#include <zmk/virtual_key_position.h> #include <zmk/virtual_key_position.h>
#include <zmk/events/position_state_changed.h> #include <zmk/events/position_state_changed.h>
#include "behavior_sensor_rotate_common.h" #include "behavior_sensor_rotate_common.h"
LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL); LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL);
int zmk_behavior_sensor_rotate_common_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);
struct behavior_sensor_rotate_data *data = dev->data;
const struct sensor_value value = channel_data[0].value;
int triggers;
int sensor_index = ZMK_SENSOR_POSITION_FROM_VIRTUAL_KEY_POSITION(event.position);
// Some funky special casing for "old encoder behavior" where ticks where reported in val2 only,
// instead of rotational degrees in val1.
// REMOVE ME: Remove after a grace period of old ec11 sensor behavior
if (value.val1 == 0) {
triggers = value.val2;
} else {
struct sensor_value remainder = data->remainder[sensor_index][event.layer];
remainder.val1 += value.val1;
remainder.val2 += value.val2;
if (abs(remainder.val2) >= 1000000) {
remainder.val1 += remainder.val2 / 1000000;
remainder.val2 %= 1000000;
}
int trigger_degrees = 360 / sensor_config->triggers_per_rotation;
triggers = remainder.val1 / trigger_degrees;
remainder.val1 %= trigger_degrees;
data->remainder[sensor_index][event.layer] = remainder;
}
LOG_DBG(
"val1: %d, val2: %d, remainder: %d/%d triggers: %d inc keycode 0x%02X dec keycode 0x%02X",
value.val1, value.val2, data->remainder[sensor_index][event.layer].val1,
data->remainder[sensor_index][event.layer].val2, triggers, binding->param1,
binding->param2);
data->triggers[sensor_index][event.layer] = triggers;
return 0;
}
int zmk_behavior_sensor_rotate_common_process(struct zmk_behavior_binding *binding, int zmk_behavior_sensor_rotate_common_process(struct zmk_behavior_binding *binding,
struct zmk_behavior_binding_event event, struct zmk_behavior_binding_event event) {
enum behavior_sensor_binding_process_mode mode) {
const struct device *dev = zmk_behavior_get_binding(binding->behavior_dev); const struct device *dev = zmk_behavior_get_binding(binding->behavior_dev);
const struct behavior_sensor_rotate_config *cfg = dev->config; const struct behavior_sensor_rotate_config *cfg = dev->config;
struct behavior_sensor_rotate_data *data = dev->data;
const int sensor_index = ZMK_SENSOR_POSITION_FROM_VIRTUAL_KEY_POSITION(event.position); const int sensor_index = ZMK_SENSOR_POSITION_FROM_VIRTUAL_KEY_POSITION(event.position);
if (mode != BEHAVIOR_SENSOR_BINDING_PROCESS_MODE_TRIGGER) { struct zmk_sensor_data *data = zmk_sensor_get_data(sensor_index);
data->triggers[sensor_index][event.layer] = 0; int triggers = data->num_triggers;
return ZMK_BEHAVIOR_TRANSPARENT;
}
int triggers = data->triggers[sensor_index][event.layer];
struct zmk_behavior_binding triggered_binding; struct zmk_behavior_binding triggered_binding;
if (triggers > 0) { if (triggers > 0) {
@ -85,7 +35,7 @@ int zmk_behavior_sensor_rotate_common_process(struct zmk_behavior_binding *bindi
triggered_binding.param1 = binding->param2; triggered_binding.param1 = binding->param2;
} }
} else { } else {
return ZMK_BEHAVIOR_TRANSPARENT; return 0;
} }
LOG_DBG("Sensor binding: %s", binding->behavior_dev); LOG_DBG("Sensor binding: %s", binding->behavior_dev);
@ -99,6 +49,5 @@ int zmk_behavior_sensor_rotate_common_process(struct zmk_behavior_binding *bindi
zmk_behavior_queue_add(&event, triggered_binding, true, cfg->tap_ms); zmk_behavior_queue_add(&event, triggered_binding, true, cfg->tap_ms);
zmk_behavior_queue_add(&event, triggered_binding, false, 0); zmk_behavior_queue_add(&event, triggered_binding, false, 0);
} }
return 0;
return ZMK_BEHAVIOR_OPAQUE;
} }

View File

@ -6,8 +6,6 @@
#include <drivers/behavior.h> #include <drivers/behavior.h>
#include <zmk/behavior.h> #include <zmk/behavior.h>
#include <zmk/keymap.h>
#include <zmk/sensors.h>
struct behavior_sensor_rotate_config { struct behavior_sensor_rotate_config {
struct zmk_behavior_binding cw_binding; struct zmk_behavior_binding cw_binding;
@ -16,15 +14,5 @@ struct behavior_sensor_rotate_config {
bool override_params; bool override_params;
}; };
struct behavior_sensor_rotate_data {
struct sensor_value remainder[ZMK_KEYMAP_SENSORS_LEN][ZMK_KEYMAP_LAYERS_LEN];
int triggers[ZMK_KEYMAP_SENSORS_LEN][ZMK_KEYMAP_LAYERS_LEN];
};
int zmk_behavior_sensor_rotate_common_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);
int zmk_behavior_sensor_rotate_common_process(struct zmk_behavior_binding *binding, int zmk_behavior_sensor_rotate_common_process(struct zmk_behavior_binding *binding,
struct zmk_behavior_binding_event event, struct zmk_behavior_binding_event event);
enum behavior_sensor_binding_process_mode mode);

View File

@ -13,7 +13,6 @@
#include "behavior_sensor_rotate_common.h" #include "behavior_sensor_rotate_common.h"
static const struct behavior_driver_api behavior_sensor_rotate_var_driver_api = { static const struct behavior_driver_api behavior_sensor_rotate_var_driver_api = {
.sensor_binding_accept_data = zmk_behavior_sensor_rotate_common_accept_data,
.sensor_binding_process = zmk_behavior_sensor_rotate_common_process}; .sensor_binding_process = zmk_behavior_sensor_rotate_common_process};
#define SENSOR_ROTATE_VAR_INST(n) \ #define SENSOR_ROTATE_VAR_INST(n) \
@ -23,10 +22,8 @@ static const struct behavior_driver_api behavior_sensor_rotate_var_driver_api =
.tap_ms = DT_INST_PROP(n, tap_ms), \ .tap_ms = DT_INST_PROP(n, tap_ms), \
.override_params = true, \ .override_params = true, \
}; \ }; \
static struct behavior_sensor_rotate_data behavior_sensor_rotate_var_data_##n = {}; \ BEHAVIOR_DT_INST_DEFINE(n, NULL, NULL, NULL, &behavior_sensor_rotate_var_config_##n, \
BEHAVIOR_DT_INST_DEFINE(n, NULL, NULL, &behavior_sensor_rotate_var_data_##n, \ POST_KERNEL, CONFIG_KERNEL_INIT_PRIORITY_DEFAULT, \
&behavior_sensor_rotate_var_config_##n, POST_KERNEL, \
CONFIG_KERNEL_INIT_PRIORITY_DEFAULT, \
&behavior_sensor_rotate_var_driver_api); &behavior_sensor_rotate_var_driver_api);
DT_INST_FOREACH_STATUS_OKAY(SENSOR_ROTATE_VAR_INST) DT_INST_FOREACH_STATUS_OKAY(SENSOR_ROTATE_VAR_INST)

View File

@ -40,6 +40,7 @@ struct behavior_sticky_key_config {
struct active_sticky_key { struct active_sticky_key {
uint32_t position; uint32_t position;
uint8_t layer;
#if IS_ENABLED(CONFIG_ZMK_SPLIT) #if IS_ENABLED(CONFIG_ZMK_SPLIT)
uint8_t source; uint8_t source;
#endif #endif
@ -67,6 +68,7 @@ static struct active_sticky_key *store_sticky_key(struct zmk_behavior_binding_ev
continue; continue;
} }
sticky_key->position = event->position; sticky_key->position = event->position;
sticky_key->layer = event->layer;
#if IS_ENABLED(CONFIG_ZMK_SPLIT) #if IS_ENABLED(CONFIG_ZMK_SPLIT)
sticky_key->source = event->source; sticky_key->source = event->source;
#endif #endif
@ -108,13 +110,16 @@ static inline int press_sticky_key_behavior(struct active_sticky_key *sticky_key
.param1 = sticky_key->param1, .param1 = sticky_key->param1,
}; };
struct zmk_behavior_binding_event event = { struct zmk_behavior_binding_event event = {
.binding = &binding,
.position = sticky_key->position, .position = sticky_key->position,
.layer = sticky_key->layer,
.timestamp = timestamp, .timestamp = timestamp,
.type = PRESS,
#if IS_ENABLED(CONFIG_ZMK_SPLIT) #if IS_ENABLED(CONFIG_ZMK_SPLIT)
.source = sticky_key->source, .source = sticky_key->source,
#endif #endif
}; };
return zmk_behavior_invoke_binding(&binding, event, true); return raise_zmk_behavior_binding_event(event);
} }
static inline int release_sticky_key_behavior(struct active_sticky_key *sticky_key, static inline int release_sticky_key_behavior(struct active_sticky_key *sticky_key,
@ -124,15 +129,18 @@ static inline int release_sticky_key_behavior(struct active_sticky_key *sticky_k
.param1 = sticky_key->param1, .param1 = sticky_key->param1,
}; };
struct zmk_behavior_binding_event event = { struct zmk_behavior_binding_event event = {
.binding = &binding,
.position = sticky_key->position, .position = sticky_key->position,
.layer = sticky_key->layer,
.timestamp = timestamp, .timestamp = timestamp,
.type = RELEASE,
#if IS_ENABLED(CONFIG_ZMK_SPLIT) #if IS_ENABLED(CONFIG_ZMK_SPLIT)
.source = sticky_key->source, .source = sticky_key->source,
#endif #endif
}; };
clear_sticky_key(sticky_key); clear_sticky_key(sticky_key);
return zmk_behavior_invoke_binding(&binding, event, false); return raise_zmk_behavior_binding_event(event);
} }
static inline void on_sticky_key_timeout(struct active_sticky_key *sticky_key) { static inline void on_sticky_key_timeout(struct active_sticky_key *sticky_key) {

View File

@ -35,6 +35,7 @@ struct active_tap_dance {
// Tap Dance Data // Tap Dance Data
int counter; int counter;
uint32_t position; uint32_t position;
uint8_t layer;
#if IS_ENABLED(CONFIG_ZMK_SPLIT) #if IS_ENABLED(CONFIG_ZMK_SPLIT)
uint8_t source; uint8_t source;
#endif #endif
@ -70,6 +71,7 @@ static int new_tap_dance(struct zmk_behavior_binding_event *event,
if (ref_dance->position == ZMK_BHV_TAP_DANCE_POSITION_FREE) { if (ref_dance->position == ZMK_BHV_TAP_DANCE_POSITION_FREE) {
ref_dance->counter = 0; ref_dance->counter = 0;
ref_dance->position = event->position; ref_dance->position = event->position;
ref_dance->layer = event->layer;
#if IS_ENABLED(CONFIG_ZMK_SPLIT) #if IS_ENABLED(CONFIG_ZMK_SPLIT)
ref_dance->source = event->source; ref_dance->source = event->source;
#endif #endif
@ -113,27 +115,34 @@ static inline int press_tap_dance_behavior(struct active_tap_dance *tap_dance, i
tap_dance->tap_dance_decided = true; tap_dance->tap_dance_decided = true;
struct zmk_behavior_binding binding = tap_dance->config->behaviors[tap_dance->counter - 1]; struct zmk_behavior_binding binding = tap_dance->config->behaviors[tap_dance->counter - 1];
struct zmk_behavior_binding_event event = { struct zmk_behavior_binding_event event = {
.binding = &binding,
.position = tap_dance->position, .position = tap_dance->position,
.timestamp = timestamp, .timestamp = timestamp,
.layer = tap_dance->layer,
.type = PRESS,
#if IS_ENABLED(CONFIG_ZMK_SPLIT) #if IS_ENABLED(CONFIG_ZMK_SPLIT)
.source = tap_dance->source, .source = tap_dance->source,
#endif #endif
}; };
return zmk_behavior_invoke_binding(&binding, event, true); return raise_zmk_behavior_binding_event(event);
} }
static inline int release_tap_dance_behavior(struct active_tap_dance *tap_dance, static inline int release_tap_dance_behavior(struct active_tap_dance *tap_dance,
int64_t timestamp) { int64_t timestamp) {
struct zmk_behavior_binding binding = tap_dance->config->behaviors[tap_dance->counter - 1]; struct zmk_behavior_binding binding = tap_dance->config->behaviors[tap_dance->counter - 1];
struct zmk_behavior_binding_event event = { struct zmk_behavior_binding_event event = {
.binding = &binding,
.position = tap_dance->position, .position = tap_dance->position,
.timestamp = timestamp, .timestamp = timestamp,
.layer = tap_dance->layer,
.type = RELEASE,
#if IS_ENABLED(CONFIG_ZMK_SPLIT) #if IS_ENABLED(CONFIG_ZMK_SPLIT)
.source = tap_dance->source, .source = tap_dance->source,
#endif #endif
}; };
clear_tap_dance(tap_dance); clear_tap_dance(tap_dance);
return zmk_behavior_invoke_binding(&binding, event, false); return raise_zmk_behavior_binding_event(event);
} }
static int on_tap_dance_binding_pressed(struct zmk_behavior_binding *binding, static int on_tap_dance_binding_pressed(struct zmk_behavior_binding *binding,

View File

@ -9,6 +9,7 @@
#include <zephyr/device.h> #include <zephyr/device.h>
#include <drivers/behavior.h> #include <drivers/behavior.h>
#include <zephyr/logging/log.h> #include <zephyr/logging/log.h>
#include <zmk/keymap.h>
#include <zmk/behavior.h> #include <zmk/behavior.h>
@ -16,19 +17,20 @@ 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)
static int on_keymap_binding_pressed(struct zmk_behavior_binding *binding, static int on_keymap_binding_trigger(struct zmk_behavior_binding *binding,
struct zmk_behavior_binding_event event) { struct zmk_behavior_binding_event event) {
return ZMK_BEHAVIOR_TRANSPARENT; // Avoid uint8_t overflow resulting in an infinite loop
} if (LAYER_ID_TO_INDEX(event.layer) == 0) {
return 0;
static int on_keymap_binding_released(struct zmk_behavior_binding *binding, }
struct zmk_behavior_binding_event event) { return zmk_keymap_raise_binding_event_at_layer_index(LAYER_ID_TO_INDEX(event.layer) - 1,
return ZMK_BEHAVIOR_TRANSPARENT; event.source, event.position, event.type,
event.timestamp);
} }
static const struct behavior_driver_api behavior_transparent_driver_api = { static const struct behavior_driver_api behavior_transparent_driver_api = {
.binding_pressed = on_keymap_binding_pressed, .binding_pressed = on_keymap_binding_trigger,
.binding_released = on_keymap_binding_released, .binding_released = on_keymap_binding_trigger,
#if IS_ENABLED(CONFIG_ZMK_BEHAVIOR_METADATA) #if IS_ENABLED(CONFIG_ZMK_BEHAVIOR_METADATA)
.get_parameter_metadata = zmk_behavior_get_empty_param_metadata, .get_parameter_metadata = zmk_behavior_get_empty_param_metadata,
#endif // IS_ENABLED(CONFIG_ZMK_BEHAVIOR_METADATA) #endif // IS_ENABLED(CONFIG_ZMK_BEHAVIOR_METADATA)

View File

@ -281,8 +281,11 @@ static int release_pressed_keys() {
static inline int press_combo_behavior(int combo_idx, const struct combo_cfg *combo, static inline int press_combo_behavior(int combo_idx, const struct combo_cfg *combo,
int32_t timestamp) { int32_t timestamp) {
struct zmk_behavior_binding_event event = { struct zmk_behavior_binding_event event = {
.binding = &combo->behavior,
.layer = 0, // Combos don't have layers, so their layer is set to be the base layer.
.position = ZMK_VIRTUAL_KEY_POSITION_COMBO(combo_idx), .position = ZMK_VIRTUAL_KEY_POSITION_COMBO(combo_idx),
.timestamp = timestamp, .timestamp = timestamp,
.type = PRESS,
#if IS_ENABLED(CONFIG_ZMK_SPLIT) #if IS_ENABLED(CONFIG_ZMK_SPLIT)
.source = ZMK_POSITION_STATE_CHANGE_SOURCE_LOCAL, .source = ZMK_POSITION_STATE_CHANGE_SOURCE_LOCAL,
#endif #endif
@ -290,20 +293,23 @@ static inline int press_combo_behavior(int combo_idx, const struct combo_cfg *co
last_combo_timestamp = timestamp; last_combo_timestamp = timestamp;
return zmk_behavior_invoke_binding(&combo->behavior, event, true); return raise_zmk_behavior_binding_event(event);
} }
static inline int release_combo_behavior(int combo_idx, const struct combo_cfg *combo, static inline int release_combo_behavior(int combo_idx, const struct combo_cfg *combo,
int32_t timestamp) { int32_t timestamp) {
struct zmk_behavior_binding_event event = { struct zmk_behavior_binding_event event = {
.binding = &combo->behavior,
.layer = 0, // Combos don't have layers, so their layer is set to be the base layer.
.position = ZMK_VIRTUAL_KEY_POSITION_COMBO(combo_idx), .position = ZMK_VIRTUAL_KEY_POSITION_COMBO(combo_idx),
.timestamp = timestamp, .timestamp = timestamp,
.type = RELEASE,
#if IS_ENABLED(CONFIG_ZMK_SPLIT) #if IS_ENABLED(CONFIG_ZMK_SPLIT)
.source = ZMK_POSITION_STATE_CHANGE_SOURCE_LOCAL, .source = ZMK_POSITION_STATE_CHANGE_SOURCE_LOCAL,
#endif #endif
}; };
return zmk_behavior_invoke_binding(&combo->behavior, event, false); return raise_zmk_behavior_binding_event(event);
} }
static void move_pressed_keys_to_active_combo(struct active_combo *active_combo) { static void move_pressed_keys_to_active_combo(struct active_combo *active_combo) {

View File

@ -0,0 +1,10 @@
/*
* Copyright (c) 2025 The ZMK Contributors
*
* SPDX-License-Identifier: MIT
*/
#include <zephyr/kernel.h>
#include <zmk/events/behavior_binding_event.h>
ZMK_EVENT_IMPL(zmk_behavior_binding_event);

View File

@ -22,6 +22,7 @@ LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL);
#include <zmk/events/position_state_changed.h> #include <zmk/events/position_state_changed.h>
#include <zmk/events/layer_state_changed.h> #include <zmk/events/layer_state_changed.h>
#include <zmk/events/sensor_event.h> #include <zmk/events/sensor_event.h>
#include <zmk/events/behavior_binding_event.h>
static zmk_keymap_layers_state_t _zmk_keymap_layer_locks = 0; static zmk_keymap_layers_state_t _zmk_keymap_layer_locks = 0;
static zmk_keymap_layers_state_t _zmk_keymap_layer_state = 0; static zmk_keymap_layers_state_t _zmk_keymap_layer_state = 0;
@ -111,7 +112,7 @@ static struct zmk_behavior_binding
#if IS_ENABLED(CONFIG_ZMK_KEYMAP_LAYER_REORDERING) #if IS_ENABLED(CONFIG_ZMK_KEYMAP_LAYER_REORDERING)
uint8_t map_layer_id_to_index(zmk_keymap_layer_id_t layer_id) { uint8_t zmk_keymap_map_layer_id_to_index(zmk_keymap_layer_id_t layer_id) {
for (uint8_t i = 0; i < ZMK_KEYMAP_LAYERS_LEN; i++) { for (uint8_t i = 0; i < ZMK_KEYMAP_LAYERS_LEN; i++) {
if (keymap_layer_orders[i] == layer_id) { if (keymap_layer_orders[i] == layer_id) {
return i; return i;
@ -122,12 +123,10 @@ uint8_t map_layer_id_to_index(zmk_keymap_layer_id_t layer_id) {
} }
#define LAYER_INDEX_TO_ID(_layer) keymap_layer_orders[_layer] #define LAYER_INDEX_TO_ID(_layer) keymap_layer_orders[_layer]
#define LAYER_ID_TO_INDEX(_layer) map_layer_id_to_index(_layer)
#else #else
#define LAYER_INDEX_TO_ID(_layer) _layer #define LAYER_INDEX_TO_ID(_layer) _layer
#define LAYER_ID_TO_INDEX(_layer) _layer
#endif // IS_ENABLED(CONFIG_ZMK_KEYMAP_LAYER_REORDERING) #endif // IS_ENABLED(CONFIG_ZMK_KEYMAP_LAYER_REORDERING)
@ -699,146 +698,82 @@ int zmk_keymap_reset_settings(void) { return -ENOTSUP; }
#endif // IS_ENABLED(CONFIG_ZMK_KEYMAP_SETTINGS_STORAGE) #endif // IS_ENABLED(CONFIG_ZMK_KEYMAP_SETTINGS_STORAGE)
int zmk_keymap_apply_position_state(uint8_t source, zmk_keymap_layer_id_t layer_id, int zmk_keymap_raise_binding_event_at_layer_index(zmk_keymap_layer_id_t layer_index, uint8_t source,
uint32_t position, bool pressed, int64_t timestamp) { uint32_t position, enum trigger_type type,
const struct zmk_behavior_binding *binding = int64_t timestamp) {
zmk_keymap_get_layer_binding_at_idx(layer_id, position);
struct zmk_behavior_binding_event event = {
.layer = layer_id,
.position = position,
.timestamp = timestamp,
#if IS_ENABLED(CONFIG_ZMK_SPLIT)
.source = source,
#endif
};
LOG_DBG("layer_id: %d position: %d, binding name: %s", layer_id, position,
binding->behavior_dev);
return zmk_behavior_invoke_binding(binding, event, pressed);
}
int zmk_keymap_position_state_changed(uint8_t source, uint32_t position, bool pressed,
int64_t timestamp) {
if (pressed) {
zmk_keymap_active_behavior_layer[position] = _zmk_keymap_layer_state;
}
// We use int here to be sure we don't loop layer_idx back to UINT8_MAX // We use int here to be sure we don't loop layer_idx back to UINT8_MAX
for (int layer_idx = ZMK_KEYMAP_LAYERS_LEN - 1; for (int layer_idx = layer_index; layer_idx >= 0; layer_idx--) {
layer_idx >= LAYER_ID_TO_INDEX(_zmk_keymap_layer_default); layer_idx--) { zmk_keymap_layer_id_t candidate_layer = LAYER_INDEX_TO_ID(layer_idx);
zmk_keymap_layer_id_t layer_id = LAYER_INDEX_TO_ID(layer_idx);
if (layer_id == ZMK_KEYMAP_LAYER_ID_INVAL) { if (candidate_layer >= ZMK_KEYMAP_LAYERS_LEN) {
continue; continue;
} }
if (zmk_keymap_layer_active_with_state(layer_id, if (zmk_keymap_layer_active_with_state(candidate_layer,
zmk_keymap_active_behavior_layer[position])) { zmk_keymap_active_behavior_layer[position]) ||
int ret = (zmk_keymap_layer_active(candidate_layer) && (position >= ZMK_KEYMAP_LEN))) {
zmk_keymap_apply_position_state(source, layer_id, position, pressed, timestamp); const struct zmk_behavior_binding *binding;
if (ret > 0) { if (position >= ZMK_KEYMAP_LEN) {
LOG_DBG("behavior processing to continue to next layer"); #if ZMK_KEYMAP_HAS_SENSORS
continue; int sensor_index = ZMK_SENSOR_POSITION_FROM_VIRTUAL_KEY_POSITION(position);
} else if (ret < 0) { binding = &zmk_sensor_keymap[candidate_layer][sensor_index];
LOG_DBG("Behavior returned error: %d", ret); if (!zmk_behavior_get_binding(binding->behavior_dev)) {
return ret; LOG_DBG("No behavior assigned to sensor index %d on layer %d", sensor_index,
candidate_layer);
continue;
}
LOG_DBG("layer_id: %d sensor_index: %d, binding name: %s", candidate_layer,
sensor_index, binding->behavior_dev);
#else
return -ENOTSUP;
#endif
} else { } else {
return ret; binding = zmk_keymap_get_layer_binding_at_idx(candidate_layer, position);
LOG_DBG("layer_id: %d position: %d, binding name: %s", candidate_layer, position,
binding->behavior_dev);
} }
return raise_zmk_behavior_binding_event((struct zmk_behavior_binding_event){
.binding = binding,
.layer = candidate_layer,
.position = position,
.timestamp = timestamp,
.type = type,
#if IS_ENABLED(CONFIG_ZMK_SPLIT)
.source = source,
#endif
});
} }
} }
return -ENOTSUP; return -ENOTSUP;
} }
#if ZMK_KEYMAP_HAS_SENSORS static int zmk_keymap_position_state_changed(uint8_t source, uint32_t position, bool pressed,
int zmk_keymap_sensor_event(uint8_t sensor_index, int64_t timestamp) {
const struct zmk_sensor_channel_data *channel_data, if (pressed) {
size_t channel_data_size, int64_t timestamp) { zmk_keymap_active_behavior_layer[position] = _zmk_keymap_layer_state;
bool opaque_response = false;
for (int layer_idx = ZMK_KEYMAP_LAYERS_LEN - 1; layer_idx >= 0; layer_idx--) {
uint8_t layer_id = LAYER_INDEX_TO_ID(layer_idx);
if (layer_id >= ZMK_KEYMAP_LAYERS_LEN) {
continue;
}
struct zmk_behavior_binding *binding = &zmk_sensor_keymap[layer_id][sensor_index];
LOG_DBG("layer idx: %d, layer id: %d sensor_index: %d, binding name: %s", layer_idx,
layer_id, sensor_index, binding->behavior_dev);
const struct device *behavior = zmk_behavior_get_binding(binding->behavior_dev);
if (!behavior) {
LOG_DBG("No behavior assigned to %d on layer %d", sensor_index, layer_id);
continue;
}
struct zmk_behavior_binding_event event = {
.layer = layer_id,
.position = ZMK_VIRTUAL_KEY_POSITION_SENSOR(sensor_index),
.timestamp = timestamp,
};
int ret = behavior_sensor_keymap_binding_accept_data(
binding, event, zmk_sensors_get_config_at_index(sensor_index), channel_data_size,
channel_data);
if (ret < 0) {
LOG_WRN("behavior data accept for behavior %s returned an error (%d). Processing to "
"continue to next layer",
binding->behavior_dev, ret);
continue;
}
enum behavior_sensor_binding_process_mode mode =
(!opaque_response && layer_idx >= LAYER_ID_TO_INDEX(_zmk_keymap_layer_default) &&
zmk_keymap_layer_active(layer_id))
? BEHAVIOR_SENSOR_BINDING_PROCESS_MODE_TRIGGER
: BEHAVIOR_SENSOR_BINDING_PROCESS_MODE_DISCARD;
ret = behavior_sensor_keymap_binding_process(binding, event, mode);
if (ret == ZMK_BEHAVIOR_OPAQUE) {
LOG_DBG("sensor event processing complete, behavior response was opaque");
opaque_response = true;
} else if (ret < 0) {
LOG_DBG("Behavior returned error: %d", ret);
return ret;
}
} }
enum trigger_type type = pressed ? PRESS : RELEASE;
return 0; int ret = zmk_keymap_raise_binding_event_at_layer_index(ZMK_KEYMAP_LAYERS_LEN - 1, source,
position, type, timestamp);
if (ret < 0) {
LOG_DBG("Behavior returned error: %d", ret);
}
return ret;
} }
#endif /* ZMK_KEYMAP_HAS_SENSORS */
int keymap_listener(const zmk_event_t *eh) { int keymap_listener(const zmk_event_t *eh) {
const struct zmk_position_state_changed *pos_ev; const struct zmk_position_state_changed *pos_ev;
if ((pos_ev = as_zmk_position_state_changed(eh)) != NULL) { if ((pos_ev = as_zmk_position_state_changed(eh)) != NULL) {
return zmk_keymap_position_state_changed(pos_ev->source, pos_ev->position, pos_ev->state, return zmk_keymap_position_state_changed(pos_ev->source, pos_ev->position, pos_ev->state,
pos_ev->timestamp); pos_ev->timestamp);
} }
#if ZMK_KEYMAP_HAS_SENSORS
const struct zmk_sensor_event *sensor_ev;
if ((sensor_ev = as_zmk_sensor_event(eh)) != NULL) {
return zmk_keymap_sensor_event(sensor_ev->sensor_index, sensor_ev->channel_data,
sensor_ev->channel_data_size, sensor_ev->timestamp);
}
#endif /* ZMK_KEYMAP_HAS_SENSORS */
return -ENOTSUP; return -ENOTSUP;
} }
ZMK_LISTENER(keymap, keymap_listener); ZMK_LISTENER(keymap, keymap_listener);
ZMK_SUBSCRIPTION(keymap, zmk_position_state_changed); ZMK_SUBSCRIPTION(keymap, zmk_position_state_changed);
#if ZMK_KEYMAP_HAS_SENSORS
ZMK_SUBSCRIPTION(keymap, zmk_sensor_event);
#endif /* ZMK_KEYMAP_HAS_SENSORS */
#if IS_ENABLED(CONFIG_ZMK_KEYMAP_SETTINGS_STORAGE) #if IS_ENABLED(CONFIG_ZMK_KEYMAP_SETTINGS_STORAGE)
static int keymap_handle_set(const char *name, size_t len, settings_read_cb read_cb, void *cb_arg) { static int keymap_handle_set(const char *name, size_t len, settings_read_cb read_cb, void *cb_arg) {

View File

@ -41,9 +41,11 @@ static int ip_behaviors_handle_event(const struct device *dev, struct input_even
for (size_t i = 0; i < cfg->size; i++) { for (size_t i = 0; i < cfg->size; i++) {
if (cfg->codes[i] == event->code) { if (cfg->codes[i] == event->code) {
struct zmk_behavior_binding_event behavior_event = { struct zmk_behavior_binding_event behavior_event = {
.binding = &cfg->bindings[i],
.position = ZMK_VIRTUAL_KEY_POSITION_BEHAVIOR_INPUT_PROCESSOR( .position = ZMK_VIRTUAL_KEY_POSITION_BEHAVIOR_INPUT_PROCESSOR(
state->input_device_index, cfg->index), state->input_device_index, cfg->index),
.timestamp = k_uptime_get(), .timestamp = k_uptime_get(),
.type = event->value ? PRESS : RELEASE,
#if IS_ENABLED(CONFIG_ZMK_SPLIT) #if IS_ENABLED(CONFIG_ZMK_SPLIT)
.source = ZMK_POSITION_STATE_CHANGE_SOURCE_LOCAL, .source = ZMK_POSITION_STATE_CHANGE_SOURCE_LOCAL,
#endif #endif
@ -52,7 +54,7 @@ static int ip_behaviors_handle_event(const struct device *dev, struct input_even
LOG_DBG("FOUND A MATCHING CODE, invoke %s for position %d with %d listeners", LOG_DBG("FOUND A MATCHING CODE, invoke %s for position %d with %d listeners",
cfg->bindings[i].behavior_dev, behavior_event.position, cfg->bindings[i].behavior_dev, behavior_event.position,
ZMK_INPUT_LISTENERS_LEN); ZMK_INPUT_LISTENERS_LEN);
int ret = zmk_behavior_invoke_binding(&cfg->bindings[i], behavior_event, event->value); int ret = raise_zmk_behavior_binding_event(behavior_event);
if (ret < 0) { if (ret < 0) {
return ret; return ret;
} }

View File

@ -15,6 +15,10 @@ LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL);
#include <zmk/sensors.h> #include <zmk/sensors.h>
#include <zmk/event_manager.h> #include <zmk/event_manager.h>
#include <zmk/events/sensor_event.h> #include <zmk/events/sensor_event.h>
#include <zmk/events/position_state_changed.h>
#include <zmk/virtual_key_position.h>
#include <zmk/behavior.h>
#include <zmk/keymap.h>
#if ZMK_KEYMAP_HAS_SENSORS #if ZMK_KEYMAP_HAS_SENSORS
@ -52,6 +56,26 @@ static struct zmk_sensor_config configs[] = {
}; };
static struct sensors_item_cfg sensors[] = {LISTIFY(ZMK_KEYMAP_SENSORS_LEN, SENSOR_ITEM, (, ), 0)}; static struct sensors_item_cfg sensors[] = {LISTIFY(ZMK_KEYMAP_SENSORS_LEN, SENSOR_ITEM, (, ), 0)};
struct zmk_sensor_data sensor_data[ZMK_KEYMAP_SENSORS_LEN] = {};
struct zmk_sensor_data *zmk_sensor_get_data(uint32_t sensor_idx) {
if (sensor_idx >= ZMK_KEYMAP_SENSORS_LEN) {
return NULL;
}
return &sensor_data[sensor_idx];
};
void zmk_sensor_set_num_triggers(uint32_t sensor_idx, int num_triggers) {
if (sensor_idx < ZMK_KEYMAP_SENSORS_LEN) {
sensor_data[sensor_idx].num_triggers = num_triggers;
}
};
void zmk_sensor_set_remainder(uint32_t sensor_idx, struct sensor_value remainder) {
if (sensor_idx < ZMK_KEYMAP_SENSORS_LEN) {
sensor_data[sensor_idx].remainder = remainder;
}
};
static ATOMIC_DEFINE(pending_sensors, ZMK_KEYMAP_SENSORS_LEN); static ATOMIC_DEFINE(pending_sensors, ZMK_KEYMAP_SENSORS_LEN);
@ -118,6 +142,49 @@ static void zmk_sensors_trigger_handler(const struct device *dev,
} }
} }
#if (!IS_ENABLED(CONFIG_ZMK_SPLIT) || IS_ENABLED(CONFIG_ZMK_SPLIT_ROLE_CENTRAL))
int sensor_listener(const zmk_event_t *eh) {
const struct zmk_sensor_event *sensor_ev = as_zmk_sensor_event(eh);
if (sensor_ev == NULL) {
return -EINVAL;
}
uint32_t sensor_index = sensor_ev->sensor_index;
const struct sensor_value value = sensor_ev->channel_data[0].value;
struct zmk_sensor_data *data = zmk_sensor_get_data(sensor_index);
const struct zmk_sensor_config *sensor_config = zmk_sensors_get_config_at_index(sensor_index);
data->remainder.val1 += value.val1;
data->remainder.val2 += value.val2;
if (data->remainder.val2 >= 1000000 || data->remainder.val2 <= 1000000) {
data->remainder.val1 += data->remainder.val2 / 1000000;
data->remainder.val2 %= 1000000;
}
int trigger_degrees = 360 / sensor_config->triggers_per_rotation;
int triggers = data->remainder.val1 / trigger_degrees;
data->remainder.val1 %= trigger_degrees;
zmk_sensor_set_remainder(sensor_index, data->remainder);
zmk_sensor_set_num_triggers(sensor_index, triggers);
LOG_DBG("val1: %d, val2: %d, remainder: %d/%d triggers: %d", value.val1, value.val2,
data->remainder.val1, data->remainder.val2, triggers);
int position = ZMK_VIRTUAL_KEY_POSITION_SENSOR(sensor_index);
// Source is set to local for the time being, to be improved in the future
int ret = zmk_keymap_raise_binding_event_at_layer_index(ZMK_KEYMAP_LAYERS_LEN - 1,
ZMK_POSITION_STATE_CHANGE_SOURCE_LOCAL,
position, SENSOR, sensor_ev->timestamp);
if (ret < 0) {
LOG_DBG("Behavior returned error: %d", ret);
}
return ret;
}
ZMK_LISTENER(sensors, sensor_listener);
ZMK_SUBSCRIPTION(sensors, zmk_sensor_event);
#endif
static void zmk_sensors_init_item(uint8_t i) { static void zmk_sensors_init_item(uint8_t i) {
LOG_DBG("Init sensor at index %d", i); LOG_DBG("Init sensor at index %d", i);

View File

@ -79,7 +79,7 @@ int zmk_split_transport_central_peripheral_event_handler(
} }
int zmk_split_central_invoke_behavior(uint8_t source, struct zmk_behavior_binding *binding, int zmk_split_central_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 (!active_transport || !active_transport->api || !active_transport->api->send_command) { if (!active_transport || !active_transport->api || !active_transport->api->send_command) {
return -ENODEV; return -ENODEV;
} }
@ -93,12 +93,22 @@ int zmk_split_central_invoke_behavior(uint8_t source, struct zmk_behavior_bindin
{ {
.param1 = binding->param1, .param1 = binding->param1,
.param2 = binding->param2, .param2 = binding->param2,
.position = event.position, .position = event->position,
.event_source = event.source, .event_source = event->source,
.state = state ? 1 : 0, .state = (event->type == PRESS) ? 1 : 0,
}, },
}, },
}; };
switch (event->type) {
case PRESS:
command.data.invoke_behavior.state = 1;
break;
case RELEASE:
command.data.invoke_behavior.state = 0;
break;
default:
return -EINVAL;
}
const size_t payload_dev_size = sizeof(command.data.invoke_behavior.behavior_dev); const size_t payload_dev_size = sizeof(command.data.invoke_behavior.behavior_dev);
if (strlcpy(command.data.invoke_behavior.behavior_dev, binding->behavior_dev, if (strlcpy(command.data.invoke_behavior.behavior_dev, binding->behavior_dev,

View File

@ -1,2 +1,2 @@
s/.*hid_listener_keycode/kp/p s/.*hid_listener_keycode/kp/p
s/.*keymap_apply_position_state/pos_state/p s/.*raise_binding_event_at_layer_index/binding_ev/p

View File

@ -1,8 +1,8 @@
pos_state: layer_id: 0 position: 0, binding name: abc_macro binding_ev: layer_id: 0 position: 0, binding name: abc_macro
kp_pressed: usage_page 0x07 keycode 0x04 implicit_mods 0x00 explicit_mods 0x00 kp_pressed: usage_page 0x07 keycode 0x04 implicit_mods 0x00 explicit_mods 0x00
pos_state: layer_id: 0 position: 0, binding name: abc_macro binding_ev: layer_id: 0 position: 0, binding name: abc_macro
pos_state: layer_id: 0 position: 1, binding name: momentary_layer binding_ev: layer_id: 0 position: 1, binding name: momentary_layer
pos_state: layer_id: 0 position: 1, binding name: momentary_layer binding_ev: layer_id: 0 position: 1, binding name: momentary_layer
kp_released: usage_page 0x07 keycode 0x04 implicit_mods 0x00 explicit_mods 0x00 kp_released: usage_page 0x07 keycode 0x04 implicit_mods 0x00 explicit_mods 0x00
kp_pressed: usage_page 0x07 keycode 0x05 implicit_mods 0x00 explicit_mods 0x00 kp_pressed: usage_page 0x07 keycode 0x05 implicit_mods 0x00 explicit_mods 0x00
kp_released: usage_page 0x07 keycode 0x05 implicit_mods 0x00 explicit_mods 0x00 kp_released: usage_page 0x07 keycode 0x05 implicit_mods 0x00 explicit_mods 0x00

View File

@ -3,19 +3,28 @@
#include <dt-bindings/zmk/kscan_mock.h> #include <dt-bindings/zmk/kscan_mock.h>
/ { / {
behaviors {
ht_bal: behavior_hold_tap_balanced {
compatible = "zmk,behavior-hold-tap";
#binding-cells = <2>;
flavor = "balanced";
tapping-term-ms = <300>;
bindings = <&kp>, <&trans>;
};
};
keymap { keymap {
compatible = "zmk,keymap"; compatible = "zmk,keymap";
default_layer { default_layer {
bindings = < bindings = <
&trans &mo 1 &trans &mo 1
&kp A &none>; &kp A &kp B>;
}; };
lower_layer { lower_layer {
bindings = < bindings = <
&trans &trans &trans &trans
&trans &kp A>; &trans &ht_bal C 0>;
}; };
}; };
}; };

View File

@ -0,0 +1 @@
s/.*hid_listener_keycode/kp/p

View File

@ -0,0 +1,4 @@
kp_pressed: usage_page 0x07 keycode 0x05 implicit_mods 0x00 explicit_mods 0x00
kp_released: usage_page 0x07 keycode 0x05 implicit_mods 0x00 explicit_mods 0x00
kp_pressed: usage_page 0x07 keycode 0x06 implicit_mods 0x00 explicit_mods 0x00
kp_released: usage_page 0x07 keycode 0x06 implicit_mods 0x00 explicit_mods 0x00

View File

@ -0,0 +1,15 @@
#include <dt-bindings/zmk/keys.h>
#include <behaviors.dtsi>
#include <dt-bindings/zmk/kscan_mock.h>
#include "../behavior_keymap.dtsi"
&kscan {
events = <
ZMK_MOCK_PRESS(0,1,10)
ZMK_MOCK_PRESS(1,1,10)
ZMK_MOCK_RELEASE(1,1,10)
ZMK_MOCK_PRESS(1,1,400)
ZMK_MOCK_RELEASE(1,1,400)
ZMK_MOCK_RELEASE(0,1,10)
>;
};