Skip to content

Commit cb867f9

Browse files
petejohansonminusfivecaksoylar
authored
Feature: input processor behavior invocation (zmkfirmware#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 <[email protected]> Co-authored-by: Cem Aksoylar <[email protected]>
1 parent d0016b3 commit cb867f9

25 files changed

+456
-37
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
# Copyright (c) 2024, The ZMK Contributors
2+
# SPDX-License-Identifier: MIT
3+
4+
description: Input Processor for invoking behaviors on certain events
5+
6+
compatible: "zmk,input-processor-behaviors"
7+
8+
include: ip_zero_param.yaml
9+
10+
properties:
11+
type:
12+
type: int
13+
codes:
14+
type: array
15+
required: true
16+
bindings:
17+
type: phandle-array
18+
required: true

app/dts/input/processors.dtsi

+2-1
Original file line numberDiff line numberDiff line change
@@ -7,4 +7,5 @@
77
#include <input/processors/scaler.dtsi>
88
#include <input/processors/code_mapper.dtsi>
99
#include <input/processors/transform.dtsi>
10-
#include <input/processors/temp_layer.dtsi>
10+
#include <input/processors/temp_layer.dtsi>
11+
#include <input/processors/behaviors.dtsi>
+16
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
/*
2+
* Copyright (c) 2024 The ZMK Contributors
3+
*
4+
* SPDX-License-Identifier: MIT
5+
*/
6+
7+
#include <zephyr/dt-bindings/input/input-event-codes.h>
8+
9+
/ {
10+
zip_button_behaviors: zip_button_behaviors {
11+
compatible = "zmk,input-processor-behaviors";
12+
#input-processor-cells = <0>;
13+
codes = <INPUT_BTN_0 INPUT_BTN_1 INPUT_BTN_2>;
14+
bindings = <&none &none &none>;
15+
};
16+
};

app/include/drivers/input_processor.h

+4
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,9 @@
1313
#include <zephyr/device.h>
1414
#include <zephyr/input/input.h>
1515

16+
#define ZMK_INPUT_PROC_CONTINUE 0
17+
#define ZMK_INPUT_PROC_STOP 1
18+
1619
struct zmk_input_processor_entry {
1720
const struct device *dev;
1821
uint32_t param1;
@@ -33,6 +36,7 @@ struct zmk_input_processor_entry {
3336
}
3437

3538
struct zmk_input_processor_state {
39+
uint8_t input_device_index;
3640
int16_t *remainder;
3741
};
3842

app/include/zmk/combos.h

+16
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
/*
2+
* Copyright (c) 2020 The ZMK Contributors
3+
*
4+
* SPDX-License-Identifier: MIT
5+
*/
6+
7+
#pragma once
8+
9+
#include <zephyr/devicetree.h>
10+
11+
#define ZMK_COMBOS_UTIL_ONE(n) 1
12+
13+
#define ZMK_COMBOS_LEN \
14+
COND_CODE_1( \
15+
DT_HAS_COMPAT_STATUS_OKAY(zmk_combos), \
16+
(DT_FOREACH_CHILD_STATUS_OKAY_SEP(DT_INST(0, zmk_combos), ZMK_COMBOS_UTIL_ONE, (+))), (0))

app/include/zmk/input_listeners.h

+14
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
/*
2+
* Copyright (c) 2020 The ZMK Contributors
3+
*
4+
* SPDX-License-Identifier: MIT
5+
*/
6+
7+
#pragma once
8+
9+
#include <zephyr/devicetree.h>
10+
11+
#define ZMK_INPUT_LISTENERS_UTIL_ONE(n) 1 +
12+
13+
#define ZMK_INPUT_LISTENERS_LEN \
14+
(DT_FOREACH_STATUS_OKAY(zmk_input_listener, ZMK_INPUT_LISTENERS_UTIL_ONE) 0)

app/include/zmk/virtual_key_position.h

+8-1
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@
77
#pragma once
88

99
#include <zmk/matrix.h>
10+
#include <zmk/combos.h>
11+
#include <zmk/input_listeners.h>
1012
#include <zmk/sensors.h>
1113

1214
/**
@@ -22,4 +24,9 @@
2224
/**
2325
* Gets the virtual key position to use for the combo with the given index.
2426
*/
25-
#define ZMK_VIRTUAL_KEY_POSITION_COMBO(index) (ZMK_KEYMAP_LEN + ZMK_KEYMAP_SENSORS_LEN + (index))
27+
#define ZMK_VIRTUAL_KEY_POSITION_COMBO(index) \
28+
(ZMK_VIRTUAL_KEY_POSITION_SENSOR(ZMK_KEYMAP_SENSORS_LEN) + (index))
29+
30+
#define ZMK_VIRTUAL_KEY_POSITION_BEHAVIOR_INPUT_PROCESSOR(listener_index, processor_index) \
31+
(ZMK_VIRTUAL_KEY_POSITION_COMBO(ZMK_COMBOS_LEN) + \
32+
(ZMK_INPUT_LISTENERS_LEN * (processor_index)) + (listener_index))

app/src/pointing/CMakeLists.txt

+1
Original file line numberDiff line numberDiff line change
@@ -6,5 +6,6 @@ target_sources_ifdef(CONFIG_ZMK_INPUT_PROCESSOR_TRANSFORM app PRIVATE input_proc
66
target_sources_ifdef(CONFIG_ZMK_INPUT_PROCESSOR_SCALER app PRIVATE input_processor_scaler.c)
77
target_sources_ifdef(CONFIG_ZMK_INPUT_PROCESSOR_TEMP_LAYER app PRIVATE input_processor_temp_layer.c)
88
target_sources_ifdef(CONFIG_ZMK_INPUT_PROCESSOR_CODE_MAPPER app PRIVATE input_processor_code_mapper.c)
9+
target_sources_ifdef(CONFIG_ZMK_INPUT_PROCESSOR_BEHAVIORS app PRIVATE input_processor_behaviors.c)
910
target_sources_ifdef(CONFIG_ZMK_POINTING_SMOOTH_SCROLLING app PRIVATE resolution_multipliers.c)
1011
target_sources_ifdef(CONFIG_ZMK_INPUT_SPLIT app PRIVATE input_split.c)

app/src/pointing/Kconfig

+5
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,11 @@ config ZMK_INPUT_PROCESSOR_CODE_MAPPER
5757
default y
5858
depends on DT_HAS_ZMK_INPUT_PROCESSOR_CODE_MAPPER_ENABLED
5959

60+
config ZMK_INPUT_PROCESSOR_BEHAVIORS
61+
bool "Behaviors Input Processor"
62+
default y
63+
depends on DT_HAS_ZMK_INPUT_PROCESSOR_BEHAVIORS_ENABLED
64+
6065
config ZMK_INPUT_SPLIT
6166
bool "Split input support"
6267
default y

app/src/pointing/input_listener.c

+40-14
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,7 @@ struct input_listener_processor_data {
7171
};
7272

7373
struct input_listener_config {
74+
uint8_t listener_index;
7475
struct input_listener_config_entry base;
7576
size_t layer_overrides_len;
7677
struct input_listener_layer_override layer_overrides[];
@@ -152,9 +153,9 @@ static inline bool is_y_data(const struct input_event *evt) {
152153
return evt->type == INPUT_EV_REL && evt->code == INPUT_REL_Y;
153154
}
154155

155-
static void apply_config(const struct input_listener_config_entry *cfg,
156-
struct input_listener_processor_data *processor_data,
157-
struct input_listener_data *data, struct input_event *evt) {
156+
static int apply_config(uint8_t listener_index, const struct input_listener_config_entry *cfg,
157+
struct input_listener_processor_data *processor_data,
158+
struct input_listener_data *data, struct input_event *evt) {
158159
size_t remainder_index = 0;
159160
for (size_t p = 0; p < cfg->processors_len; p++) {
160161
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,
183184
}
184185
}
185186

186-
struct zmk_input_processor_state state = {.remainder = remainder};
187-
188-
zmk_input_processor_handle_event(proc_e->dev, evt, proc_e->param1, proc_e->param2, &state);
187+
LOG_DBG("LISTENER INDEX: %d", listener_index);
188+
struct zmk_input_processor_state state = {.input_device_index = listener_index,
189+
.remainder = remainder};
190+
191+
int ret = zmk_input_processor_handle_event(proc_e->dev, evt, proc_e->param1, proc_e->param2,
192+
&state);
193+
switch (ret) {
194+
case ZMK_INPUT_PROC_CONTINUE:
195+
continue;
196+
default:
197+
return ret;
198+
}
189199
}
200+
201+
return ZMK_INPUT_PROC_CONTINUE;
190202
}
191-
static void filter_with_input_config(const struct input_listener_config *cfg,
192-
struct input_listener_data *data, struct input_event *evt) {
203+
204+
static int filter_with_input_config(const struct input_listener_config *cfg,
205+
struct input_listener_data *data, struct input_event *evt) {
193206
if (!evt->dev) {
194-
return;
207+
return -ENODEV;
195208
}
196209

197210
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,
201214
uint8_t layer = 0;
202215
while (mask != 0) {
203216
if (mask & BIT(0) && zmk_keymap_layer_active(layer)) {
204-
apply_config(&override->config, override_data, data, evt);
217+
int ret =
218+
apply_config(cfg->listener_index, &override->config, override_data, data, evt);
219+
220+
if (ret < 0) {
221+
return ret;
222+
}
205223
if (!override->process_next) {
206-
return;
224+
return 0;
207225
}
208226
}
209227

@@ -212,7 +230,7 @@ static void filter_with_input_config(const struct input_listener_config *cfg,
212230
}
213231
}
214232

215-
apply_config(&cfg->base, &data->base_processor_data, data, evt);
233+
return apply_config(cfg->listener_index, &cfg->base, &data->base_processor_data, data, evt);
216234
}
217235

218236
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
247265

248266
static void input_handler(const struct input_listener_config *config,
249267
struct input_listener_data *data, struct input_event *evt) {
250-
// First, filter to update the event data as needed.
251-
filter_with_input_config(config, data, evt);
268+
// First, process to update the event data as needed.
269+
int ret = filter_with_input_config(config, data, evt);
270+
271+
if (ret < 0) {
272+
LOG_ERR("Error applying input processors: %d", ret);
273+
return;
274+
} else if (ret == ZMK_INPUT_PROC_STOP) {
275+
return;
276+
}
252277

253278
#if IS_ENABLED(CONFIG_ZMK_POINTING_SMOOTH_SCROLLING)
254279
apply_resolution_scaling(data, evt);
@@ -355,6 +380,7 @@ static void input_handler(const struct input_listener_config *config,
355380
DT_INST_FOREACH_CHILD_VARGS(n, CHILD_CONFIG, \
356381
n) static const struct input_listener_config config_##n = \
357382
{ \
383+
.listener_index = n, \
358384
.base = IL_EXTRACT_CONFIG(DT_DRV_INST(n), n, base), \
359385
.layer_overrides_len = (0 DT_INST_FOREACH_CHILD(n, IL_ONE)), \
360386
.layer_overrides = {DT_INST_FOREACH_CHILD_SEP_VARGS(n, IL_OVERRIDE, (, ), n)}, \
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
/*
2+
* Copyright (c) 2024 The ZMK Contributors
3+
*
4+
* SPDX-License-Identifier: MIT
5+
*/
6+
7+
#define DT_DRV_COMPAT zmk_input_processor_behaviors
8+
9+
#include <zephyr/dt-bindings/input/input-event-codes.h>
10+
11+
#include <zephyr/kernel.h>
12+
#include <zephyr/device.h>
13+
#include <drivers/input_processor.h>
14+
15+
#include <zephyr/logging/log.h>
16+
17+
LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL);
18+
19+
#include <zmk/keymap.h>
20+
#include <zmk/behavior.h>
21+
#include <zmk/virtual_key_position.h>
22+
23+
struct ip_behaviors_config {
24+
uint8_t index;
25+
size_t size;
26+
uint16_t type;
27+
28+
const uint16_t *codes;
29+
const struct zmk_behavior_binding *bindings;
30+
};
31+
32+
static int ip_behaviors_handle_event(const struct device *dev, struct input_event *event,
33+
uint32_t param1, uint32_t param2,
34+
struct zmk_input_processor_state *state) {
35+
const struct ip_behaviors_config *cfg = dev->config;
36+
37+
if (event->type != cfg->type) {
38+
return 0;
39+
}
40+
41+
for (size_t i = 0; i < cfg->size; i++) {
42+
if (cfg->codes[i] == event->code) {
43+
struct zmk_behavior_binding_event behavior_event = {
44+
.position = ZMK_VIRTUAL_KEY_POSITION_BEHAVIOR_INPUT_PROCESSOR(
45+
state->input_device_index, cfg->index),
46+
.timestamp = k_uptime_get(),
47+
#if IS_ENABLED(CONFIG_ZMK_SPLIT)
48+
.source = ZMK_POSITION_STATE_CHANGE_SOURCE_LOCAL,
49+
#endif
50+
};
51+
52+
LOG_DBG("FOUND A MATCHING CODE, invoke %s for position %d with %d listeners",
53+
cfg->bindings[i].behavior_dev, behavior_event.position,
54+
ZMK_INPUT_LISTENERS_LEN);
55+
int ret = zmk_behavior_invoke_binding(&cfg->bindings[i], behavior_event, event->value);
56+
if (ret < 0) {
57+
return ret;
58+
}
59+
60+
return ZMK_INPUT_PROC_STOP;
61+
}
62+
}
63+
64+
return 0;
65+
}
66+
67+
static struct zmk_input_processor_driver_api ip_behaviors_driver_api = {
68+
.handle_event = ip_behaviors_handle_event,
69+
};
70+
71+
static int ip_behaviors_init(const struct device *dev) { return 0; }
72+
73+
#define ENTRY(i, node) ZMK_KEYMAP_EXTRACT_BINDING(i, node)
74+
75+
#define IP_BEHAVIORS_INST(n) \
76+
static const uint16_t ip_behaviors_codes_##n[] = DT_INST_PROP(n, codes); \
77+
static const struct zmk_behavior_binding ip_behaviors_bindings_##n[] = { \
78+
LISTIFY(DT_INST_PROP_LEN(n, bindings), ZMK_KEYMAP_EXTRACT_BINDING, (, ), DT_DRV_INST(n))}; \
79+
BUILD_ASSERT(ARRAY_SIZE(ip_behaviors_codes_##n) == ARRAY_SIZE(ip_behaviors_bindings_##n), \
80+
"codes and bindings need to be the same length"); \
81+
static const struct ip_behaviors_config ip_behaviors_config_##n = { \
82+
.index = n, \
83+
.type = DT_INST_PROP_OR(n, type, INPUT_EV_KEY), \
84+
.size = DT_INST_PROP_LEN(n, codes), \
85+
.codes = ip_behaviors_codes_##n, \
86+
.bindings = ip_behaviors_bindings_##n, \
87+
}; \
88+
DEVICE_DT_INST_DEFINE(n, &ip_behaviors_init, NULL, NULL, &ip_behaviors_config_##n, \
89+
POST_KERNEL, CONFIG_KERNEL_INIT_PRIORITY_DEFAULT, \
90+
&ip_behaviors_driver_api);
91+
92+
DT_INST_FOREACH_STATUS_OKAY(IP_BEHAVIORS_INST)

app/src/pointing/input_processor_code_mapper.c

+2-2
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ static int cm_handle_event(const struct device *dev, struct input_event *event,
2525
const struct cm_config *cfg = dev->config;
2626

2727
if (event->type != cfg->type) {
28-
return 0;
28+
return ZMK_INPUT_PROC_CONTINUE;
2929
}
3030

3131
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,
3737
}
3838
}
3939

40-
return 0;
40+
return ZMK_INPUT_PROC_CONTINUE;
4141
}
4242

4343
static struct zmk_input_processor_driver_api cm_driver_api = {

app/src/pointing/input_processor_scaler.c

+2-2
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ static int scaler_handle_event(const struct device *dev, struct input_event *eve
4747
const struct scaler_config *cfg = dev->config;
4848

4949
if (event->type != cfg->type) {
50-
return 0;
50+
return ZMK_INPUT_PROC_CONTINUE;
5151
}
5252

5353
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
5656
}
5757
}
5858

59-
return 0;
59+
return ZMK_INPUT_PROC_CONTINUE;
6060
}
6161

6262
static struct zmk_input_processor_driver_api scaler_driver_api = {

app/src/pointing/input_processor_temp_layer.c

+1-1
Original file line numberDiff line numberDiff line change
@@ -162,7 +162,7 @@ static int temp_layer_handle_event(const struct device *dev, struct input_event
162162
k_work_reschedule(&layer_disable_works[param1], K_MSEC(param2));
163163
}
164164

165-
return 0;
165+
return ZMK_INPUT_PROC_CONTINUE;
166166
}
167167

168168
static int temp_layer_init(const struct device *dev) {

0 commit comments

Comments
 (0)