forked from kofal.net/zmk
Feature: input processor behavior invocation (#2714)
refactor(pointing): Allow stopping event propagation
Allow input processors to return a special value if a given input event
should not be further processed/propagated.
feat(pointing): Add behavior input processor
Add the ability to intercept certain input events and trigger behaviors
when they occur.
Co-authored-by: Jorge Villalobos <minusfive@users.noreply.github.com>
Co-authored-by: Cem Aksoylar <caksoylar@users.noreply.github.com>
This commit is contained in:
@@ -6,5 +6,6 @@ target_sources_ifdef(CONFIG_ZMK_INPUT_PROCESSOR_TRANSFORM app PRIVATE input_proc
|
||||
target_sources_ifdef(CONFIG_ZMK_INPUT_PROCESSOR_SCALER app PRIVATE input_processor_scaler.c)
|
||||
target_sources_ifdef(CONFIG_ZMK_INPUT_PROCESSOR_TEMP_LAYER app PRIVATE input_processor_temp_layer.c)
|
||||
target_sources_ifdef(CONFIG_ZMK_INPUT_PROCESSOR_CODE_MAPPER app PRIVATE input_processor_code_mapper.c)
|
||||
target_sources_ifdef(CONFIG_ZMK_INPUT_PROCESSOR_BEHAVIORS app PRIVATE input_processor_behaviors.c)
|
||||
target_sources_ifdef(CONFIG_ZMK_POINTING_SMOOTH_SCROLLING app PRIVATE resolution_multipliers.c)
|
||||
target_sources_ifdef(CONFIG_ZMK_INPUT_SPLIT app PRIVATE input_split.c)
|
||||
|
||||
@@ -57,6 +57,11 @@ config ZMK_INPUT_PROCESSOR_CODE_MAPPER
|
||||
default y
|
||||
depends on DT_HAS_ZMK_INPUT_PROCESSOR_CODE_MAPPER_ENABLED
|
||||
|
||||
config ZMK_INPUT_PROCESSOR_BEHAVIORS
|
||||
bool "Behaviors Input Processor"
|
||||
default y
|
||||
depends on DT_HAS_ZMK_INPUT_PROCESSOR_BEHAVIORS_ENABLED
|
||||
|
||||
config ZMK_INPUT_SPLIT
|
||||
bool "Split input support"
|
||||
default y
|
||||
|
||||
@@ -71,6 +71,7 @@ struct input_listener_processor_data {
|
||||
};
|
||||
|
||||
struct input_listener_config {
|
||||
uint8_t listener_index;
|
||||
struct input_listener_config_entry base;
|
||||
size_t layer_overrides_len;
|
||||
struct input_listener_layer_override layer_overrides[];
|
||||
@@ -152,9 +153,9 @@ static inline bool is_y_data(const struct input_event *evt) {
|
||||
return evt->type == INPUT_EV_REL && evt->code == INPUT_REL_Y;
|
||||
}
|
||||
|
||||
static void apply_config(const struct input_listener_config_entry *cfg,
|
||||
struct input_listener_processor_data *processor_data,
|
||||
struct input_listener_data *data, struct input_event *evt) {
|
||||
static int apply_config(uint8_t listener_index, const struct input_listener_config_entry *cfg,
|
||||
struct input_listener_processor_data *processor_data,
|
||||
struct input_listener_data *data, struct input_event *evt) {
|
||||
size_t remainder_index = 0;
|
||||
for (size_t p = 0; p < cfg->processors_len; p++) {
|
||||
const struct zmk_input_processor_entry *proc_e = &cfg->processors[p];
|
||||
@@ -183,15 +184,27 @@ static void apply_config(const struct input_listener_config_entry *cfg,
|
||||
}
|
||||
}
|
||||
|
||||
struct zmk_input_processor_state state = {.remainder = remainder};
|
||||
LOG_DBG("LISTENER INDEX: %d", listener_index);
|
||||
struct zmk_input_processor_state state = {.input_device_index = listener_index,
|
||||
.remainder = remainder};
|
||||
|
||||
zmk_input_processor_handle_event(proc_e->dev, evt, proc_e->param1, proc_e->param2, &state);
|
||||
int ret = zmk_input_processor_handle_event(proc_e->dev, evt, proc_e->param1, proc_e->param2,
|
||||
&state);
|
||||
switch (ret) {
|
||||
case ZMK_INPUT_PROC_CONTINUE:
|
||||
continue;
|
||||
default:
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
return ZMK_INPUT_PROC_CONTINUE;
|
||||
}
|
||||
static void filter_with_input_config(const struct input_listener_config *cfg,
|
||||
struct input_listener_data *data, struct input_event *evt) {
|
||||
|
||||
static int filter_with_input_config(const struct input_listener_config *cfg,
|
||||
struct input_listener_data *data, struct input_event *evt) {
|
||||
if (!evt->dev) {
|
||||
return;
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
for (size_t oi = 0; oi < cfg->layer_overrides_len; oi++) {
|
||||
@@ -201,9 +214,14 @@ static void filter_with_input_config(const struct input_listener_config *cfg,
|
||||
uint8_t layer = 0;
|
||||
while (mask != 0) {
|
||||
if (mask & BIT(0) && zmk_keymap_layer_active(layer)) {
|
||||
apply_config(&override->config, override_data, data, evt);
|
||||
int ret =
|
||||
apply_config(cfg->listener_index, &override->config, override_data, data, evt);
|
||||
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
if (!override->process_next) {
|
||||
return;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -212,7 +230,7 @@ static void filter_with_input_config(const struct input_listener_config *cfg,
|
||||
}
|
||||
}
|
||||
|
||||
apply_config(&cfg->base, &data->base_processor_data, data, evt);
|
||||
return apply_config(cfg->listener_index, &cfg->base, &data->base_processor_data, data, evt);
|
||||
}
|
||||
|
||||
static void clear_xy_data(struct input_listener_xy_data *data) {
|
||||
@@ -247,8 +265,15 @@ static void apply_resolution_scaling(struct input_listener_data *data, struct in
|
||||
|
||||
static void input_handler(const struct input_listener_config *config,
|
||||
struct input_listener_data *data, struct input_event *evt) {
|
||||
// First, filter to update the event data as needed.
|
||||
filter_with_input_config(config, data, evt);
|
||||
// First, process to update the event data as needed.
|
||||
int ret = filter_with_input_config(config, data, evt);
|
||||
|
||||
if (ret < 0) {
|
||||
LOG_ERR("Error applying input processors: %d", ret);
|
||||
return;
|
||||
} else if (ret == ZMK_INPUT_PROC_STOP) {
|
||||
return;
|
||||
}
|
||||
|
||||
#if IS_ENABLED(CONFIG_ZMK_POINTING_SMOOTH_SCROLLING)
|
||||
apply_resolution_scaling(data, evt);
|
||||
@@ -355,6 +380,7 @@ static void input_handler(const struct input_listener_config *config,
|
||||
DT_INST_FOREACH_CHILD_VARGS(n, CHILD_CONFIG, \
|
||||
n) static const struct input_listener_config config_##n = \
|
||||
{ \
|
||||
.listener_index = n, \
|
||||
.base = IL_EXTRACT_CONFIG(DT_DRV_INST(n), n, base), \
|
||||
.layer_overrides_len = (0 DT_INST_FOREACH_CHILD(n, IL_ONE)), \
|
||||
.layer_overrides = {DT_INST_FOREACH_CHILD_SEP_VARGS(n, IL_OVERRIDE, (, ), n)}, \
|
||||
|
||||
92
app/src/pointing/input_processor_behaviors.c
Normal file
92
app/src/pointing/input_processor_behaviors.c
Normal file
@@ -0,0 +1,92 @@
|
||||
/*
|
||||
* Copyright (c) 2024 The ZMK Contributors
|
||||
*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
|
||||
#define DT_DRV_COMPAT zmk_input_processor_behaviors
|
||||
|
||||
#include <zephyr/dt-bindings/input/input-event-codes.h>
|
||||
|
||||
#include <zephyr/kernel.h>
|
||||
#include <zephyr/device.h>
|
||||
#include <drivers/input_processor.h>
|
||||
|
||||
#include <zephyr/logging/log.h>
|
||||
|
||||
LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL);
|
||||
|
||||
#include <zmk/keymap.h>
|
||||
#include <zmk/behavior.h>
|
||||
#include <zmk/virtual_key_position.h>
|
||||
|
||||
struct ip_behaviors_config {
|
||||
uint8_t index;
|
||||
size_t size;
|
||||
uint16_t type;
|
||||
|
||||
const uint16_t *codes;
|
||||
const struct zmk_behavior_binding *bindings;
|
||||
};
|
||||
|
||||
static int ip_behaviors_handle_event(const struct device *dev, struct input_event *event,
|
||||
uint32_t param1, uint32_t param2,
|
||||
struct zmk_input_processor_state *state) {
|
||||
const struct ip_behaviors_config *cfg = dev->config;
|
||||
|
||||
if (event->type != cfg->type) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < cfg->size; i++) {
|
||||
if (cfg->codes[i] == event->code) {
|
||||
struct zmk_behavior_binding_event behavior_event = {
|
||||
.position = ZMK_VIRTUAL_KEY_POSITION_BEHAVIOR_INPUT_PROCESSOR(
|
||||
state->input_device_index, cfg->index),
|
||||
.timestamp = k_uptime_get(),
|
||||
#if IS_ENABLED(CONFIG_ZMK_SPLIT)
|
||||
.source = ZMK_POSITION_STATE_CHANGE_SOURCE_LOCAL,
|
||||
#endif
|
||||
};
|
||||
|
||||
LOG_DBG("FOUND A MATCHING CODE, invoke %s for position %d with %d listeners",
|
||||
cfg->bindings[i].behavior_dev, behavior_event.position,
|
||||
ZMK_INPUT_LISTENERS_LEN);
|
||||
int ret = zmk_behavior_invoke_binding(&cfg->bindings[i], behavior_event, event->value);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
return ZMK_INPUT_PROC_STOP;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct zmk_input_processor_driver_api ip_behaviors_driver_api = {
|
||||
.handle_event = ip_behaviors_handle_event,
|
||||
};
|
||||
|
||||
static int ip_behaviors_init(const struct device *dev) { return 0; }
|
||||
|
||||
#define ENTRY(i, node) ZMK_KEYMAP_EXTRACT_BINDING(i, node)
|
||||
|
||||
#define IP_BEHAVIORS_INST(n) \
|
||||
static const uint16_t ip_behaviors_codes_##n[] = DT_INST_PROP(n, codes); \
|
||||
static const struct zmk_behavior_binding ip_behaviors_bindings_##n[] = { \
|
||||
LISTIFY(DT_INST_PROP_LEN(n, bindings), ZMK_KEYMAP_EXTRACT_BINDING, (, ), DT_DRV_INST(n))}; \
|
||||
BUILD_ASSERT(ARRAY_SIZE(ip_behaviors_codes_##n) == ARRAY_SIZE(ip_behaviors_bindings_##n), \
|
||||
"codes and bindings need to be the same length"); \
|
||||
static const struct ip_behaviors_config ip_behaviors_config_##n = { \
|
||||
.index = n, \
|
||||
.type = DT_INST_PROP_OR(n, type, INPUT_EV_KEY), \
|
||||
.size = DT_INST_PROP_LEN(n, codes), \
|
||||
.codes = ip_behaviors_codes_##n, \
|
||||
.bindings = ip_behaviors_bindings_##n, \
|
||||
}; \
|
||||
DEVICE_DT_INST_DEFINE(n, &ip_behaviors_init, NULL, NULL, &ip_behaviors_config_##n, \
|
||||
POST_KERNEL, CONFIG_KERNEL_INIT_PRIORITY_DEFAULT, \
|
||||
&ip_behaviors_driver_api);
|
||||
|
||||
DT_INST_FOREACH_STATUS_OKAY(IP_BEHAVIORS_INST)
|
||||
@@ -25,7 +25,7 @@ static int cm_handle_event(const struct device *dev, struct input_event *event,
|
||||
const struct cm_config *cfg = dev->config;
|
||||
|
||||
if (event->type != cfg->type) {
|
||||
return 0;
|
||||
return ZMK_INPUT_PROC_CONTINUE;
|
||||
}
|
||||
|
||||
for (int i = 0; i < cfg->mapping_size / 2; i++) {
|
||||
@@ -37,7 +37,7 @@ static int cm_handle_event(const struct device *dev, struct input_event *event,
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
return ZMK_INPUT_PROC_CONTINUE;
|
||||
}
|
||||
|
||||
static struct zmk_input_processor_driver_api cm_driver_api = {
|
||||
|
||||
@@ -47,7 +47,7 @@ static int scaler_handle_event(const struct device *dev, struct input_event *eve
|
||||
const struct scaler_config *cfg = dev->config;
|
||||
|
||||
if (event->type != cfg->type) {
|
||||
return 0;
|
||||
return ZMK_INPUT_PROC_CONTINUE;
|
||||
}
|
||||
|
||||
for (int i = 0; i < cfg->codes_len; i++) {
|
||||
@@ -56,7 +56,7 @@ static int scaler_handle_event(const struct device *dev, struct input_event *eve
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
return ZMK_INPUT_PROC_CONTINUE;
|
||||
}
|
||||
|
||||
static struct zmk_input_processor_driver_api scaler_driver_api = {
|
||||
|
||||
@@ -162,7 +162,7 @@ static int temp_layer_handle_event(const struct device *dev, struct input_event
|
||||
k_work_reschedule(&layer_disable_works[param1], K_MSEC(param2));
|
||||
}
|
||||
|
||||
return 0;
|
||||
return ZMK_INPUT_PROC_CONTINUE;
|
||||
}
|
||||
|
||||
static int temp_layer_init(const struct device *dev) {
|
||||
|
||||
@@ -42,7 +42,7 @@ static int ipt_handle_event(const struct device *dev, struct input_event *event,
|
||||
const struct ipt_config *cfg = dev->config;
|
||||
|
||||
if (event->type != cfg->type) {
|
||||
return 0;
|
||||
return ZMK_INPUT_PROC_CONTINUE;
|
||||
}
|
||||
|
||||
if (param1 & INPUT_TRANSFORM_XY_SWAP) {
|
||||
@@ -65,7 +65,7 @@ static int ipt_handle_event(const struct device *dev, struct input_event *event,
|
||||
event->value = -event->value;
|
||||
}
|
||||
|
||||
return 0;
|
||||
return ZMK_INPUT_PROC_CONTINUE;
|
||||
}
|
||||
|
||||
static struct zmk_input_processor_driver_api ipt_driver_api = {
|
||||
|
||||
Reference in New Issue
Block a user