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

Feature: input processor behavior invocation #2714

Merged
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
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# Copyright (c) 2024, The ZMK Contributors
# SPDX-License-Identifier: MIT

description: Input Processor for invoking behaviors on certain events

compatible: "zmk,input-processor-behaviors"

include: ip_zero_param.yaml

properties:
type:
type: int
codes:
type: array
required: true
bindings:
type: phandle-array
required: true
3 changes: 2 additions & 1 deletion app/dts/input/processors.dtsi
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,5 @@
#include <input/processors/scaler.dtsi>
#include <input/processors/code_mapper.dtsi>
#include <input/processors/transform.dtsi>
#include <input/processors/temp_layer.dtsi>
#include <input/processors/temp_layer.dtsi>
#include <input/processors/behaviors.dtsi>
16 changes: 16 additions & 0 deletions app/dts/input/processors/behaviors.dtsi
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
/*
* Copyright (c) 2024 The ZMK Contributors
*
* SPDX-License-Identifier: MIT
*/

#include <zephyr/dt-bindings/input/input-event-codes.h>

/ {
zip_button_behaviors: zip_button_behaviors {
compatible = "zmk,input-processor-behaviors";
#input-processor-cells = <0>;
codes = <INPUT_BTN_0 INPUT_BTN_1 INPUT_BTN_2>;
bindings = <&none &none &none>;
Nick-Munnich marked this conversation as resolved.
Show resolved Hide resolved
};
};
4 changes: 4 additions & 0 deletions app/include/drivers/input_processor.h
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,9 @@
#include <zephyr/device.h>
#include <zephyr/input/input.h>

#define ZMK_INPUT_PROC_CONTINUE 0
#define ZMK_INPUT_PROC_STOP 1

struct zmk_input_processor_entry {
const struct device *dev;
uint32_t param1;
Expand All @@ -33,6 +36,7 @@ struct zmk_input_processor_entry {
}

struct zmk_input_processor_state {
uint8_t input_device_index;
int16_t *remainder;
};

Expand Down
16 changes: 16 additions & 0 deletions app/include/zmk/combos.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
/*
* Copyright (c) 2020 The ZMK Contributors
*
* SPDX-License-Identifier: MIT
*/

#pragma once

#include <zephyr/devicetree.h>

#define ZMK_COMBOS_UTIL_ONE(n) 1

#define ZMK_COMBOS_LEN \
COND_CODE_1( \
DT_HAS_COMPAT_STATUS_OKAY(zmk_combos), \
(DT_FOREACH_CHILD_STATUS_OKAY_SEP(DT_INST(0, zmk_combos), ZMK_COMBOS_UTIL_ONE, (+))), (0))
14 changes: 14 additions & 0 deletions app/include/zmk/input_listeners.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
/*
* Copyright (c) 2020 The ZMK Contributors
*
* SPDX-License-Identifier: MIT
*/

#pragma once

#include <zephyr/devicetree.h>

#define ZMK_INPUT_LISTENERS_UTIL_ONE(n) 1 +

#define ZMK_INPUT_LISTENERS_LEN \
(DT_FOREACH_STATUS_OKAY(zmk_input_listener, ZMK_INPUT_LISTENERS_UTIL_ONE) 0)
9 changes: 8 additions & 1 deletion app/include/zmk/virtual_key_position.h
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@
#pragma once

#include <zmk/matrix.h>
#include <zmk/combos.h>
#include <zmk/input_listeners.h>
#include <zmk/sensors.h>

/**
Expand All @@ -22,4 +24,9 @@
/**
* Gets the virtual key position to use for the combo with the given index.
*/
#define ZMK_VIRTUAL_KEY_POSITION_COMBO(index) (ZMK_KEYMAP_LEN + ZMK_KEYMAP_SENSORS_LEN + (index))
#define ZMK_VIRTUAL_KEY_POSITION_COMBO(index) \
(ZMK_VIRTUAL_KEY_POSITION_SENSOR(ZMK_KEYMAP_SENSORS_LEN) + (index))

#define ZMK_VIRTUAL_KEY_POSITION_BEHAVIOR_INPUT_PROCESSOR(listener_index, processor_index) \
(ZMK_VIRTUAL_KEY_POSITION_COMBO(ZMK_COMBOS_LEN) + \
(ZMK_INPUT_LISTENERS_LEN * (processor_index)) + (listener_index))
1 change: 1 addition & 0 deletions app/src/pointing/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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)
5 changes: 5 additions & 0 deletions app/src/pointing/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
54 changes: 40 additions & 14 deletions app/src/pointing/input_listener.c
Original file line number Diff line number Diff line change
Expand Up @@ -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[];
Expand Down Expand Up @@ -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];
Expand Down Expand Up @@ -183,15 +184,27 @@ static void apply_config(const struct input_listener_config_entry *cfg,
}
}

struct zmk_input_processor_state state = {.remainder = remainder};

zmk_input_processor_handle_event(proc_e->dev, evt, proc_e->param1, proc_e->param2, &state);
LOG_DBG("LISTENER INDEX: %d", listener_index);
struct zmk_input_processor_state state = {.input_device_index = listener_index,
.remainder = remainder};

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++) {
Expand All @@ -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;
}
}

Expand All @@ -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) {
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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)}, \
Expand Down
92 changes: 92 additions & 0 deletions app/src/pointing/input_processor_behaviors.c
Original file line number Diff line number Diff line change
@@ -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,
caksoylar marked this conversation as resolved.
Show resolved Hide resolved
#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)
4 changes: 2 additions & 2 deletions app/src/pointing/input_processor_code_mapper.c
Original file line number Diff line number Diff line change
Expand Up @@ -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++) {
Expand All @@ -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 = {
Expand Down
4 changes: 2 additions & 2 deletions app/src/pointing/input_processor_scaler.c
Original file line number Diff line number Diff line change
Expand Up @@ -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++) {
Expand All @@ -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 = {
Expand Down
2 changes: 1 addition & 1 deletion app/src/pointing/input_processor_temp_layer.c
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down
Loading
Loading