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:
Pete Johanson 2024-12-17 18:50:06 -07:00 committed by GitHub
parent d0016b34f8
commit cb867f92db
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
25 changed files with 455 additions and 36 deletions

View File

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

View File

@ -7,4 +7,5 @@
#include <input/processors/scaler.dtsi> #include <input/processors/scaler.dtsi>
#include <input/processors/code_mapper.dtsi> #include <input/processors/code_mapper.dtsi>
#include <input/processors/transform.dtsi> #include <input/processors/transform.dtsi>
#include <input/processors/temp_layer.dtsi> #include <input/processors/temp_layer.dtsi>
#include <input/processors/behaviors.dtsi>

View File

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

View File

@ -13,6 +13,9 @@
#include <zephyr/device.h> #include <zephyr/device.h>
#include <zephyr/input/input.h> #include <zephyr/input/input.h>
#define ZMK_INPUT_PROC_CONTINUE 0
#define ZMK_INPUT_PROC_STOP 1
struct zmk_input_processor_entry { struct zmk_input_processor_entry {
const struct device *dev; const struct device *dev;
uint32_t param1; uint32_t param1;
@ -33,6 +36,7 @@ struct zmk_input_processor_entry {
} }
struct zmk_input_processor_state { struct zmk_input_processor_state {
uint8_t input_device_index;
int16_t *remainder; int16_t *remainder;
}; };

16
app/include/zmk/combos.h Normal file
View File

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

View File

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

View File

@ -7,6 +7,8 @@
#pragma once #pragma once
#include <zmk/matrix.h> #include <zmk/matrix.h>
#include <zmk/combos.h>
#include <zmk/input_listeners.h>
#include <zmk/sensors.h> #include <zmk/sensors.h>
/** /**
@ -22,4 +24,9 @@
/** /**
* Gets the virtual key position to use for the combo with the given index. * 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))

View File

@ -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_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_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_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_POINTING_SMOOTH_SCROLLING app PRIVATE resolution_multipliers.c)
target_sources_ifdef(CONFIG_ZMK_INPUT_SPLIT app PRIVATE input_split.c) target_sources_ifdef(CONFIG_ZMK_INPUT_SPLIT app PRIVATE input_split.c)

View File

@ -57,6 +57,11 @@ config ZMK_INPUT_PROCESSOR_CODE_MAPPER
default y default y
depends on DT_HAS_ZMK_INPUT_PROCESSOR_CODE_MAPPER_ENABLED 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 config ZMK_INPUT_SPLIT
bool "Split input support" bool "Split input support"
default y default y

View File

@ -71,6 +71,7 @@ struct input_listener_processor_data {
}; };
struct input_listener_config { struct input_listener_config {
uint8_t listener_index;
struct input_listener_config_entry base; struct input_listener_config_entry base;
size_t layer_overrides_len; size_t layer_overrides_len;
struct input_listener_layer_override layer_overrides[]; 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; return evt->type == INPUT_EV_REL && evt->code == INPUT_REL_Y;
} }
static void apply_config(const struct input_listener_config_entry *cfg, 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_processor_data *processor_data,
struct input_listener_data *data, struct input_event *evt) { struct input_listener_data *data, struct input_event *evt) {
size_t remainder_index = 0; size_t remainder_index = 0;
for (size_t p = 0; p < cfg->processors_len; p++) { for (size_t p = 0; p < cfg->processors_len; p++) {
const struct zmk_input_processor_entry *proc_e = &cfg->processors[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) { if (!evt->dev) {
return; return -ENODEV;
} }
for (size_t oi = 0; oi < cfg->layer_overrides_len; oi++) { 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; uint8_t layer = 0;
while (mask != 0) { while (mask != 0) {
if (mask & BIT(0) && zmk_keymap_layer_active(layer)) { 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) { 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) { 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, static void input_handler(const struct input_listener_config *config,
struct input_listener_data *data, struct input_event *evt) { struct input_listener_data *data, struct input_event *evt) {
// First, filter to update the event data as needed. // First, process to update the event data as needed.
filter_with_input_config(config, data, evt); 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) #if IS_ENABLED(CONFIG_ZMK_POINTING_SMOOTH_SCROLLING)
apply_resolution_scaling(data, evt); 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, \ DT_INST_FOREACH_CHILD_VARGS(n, CHILD_CONFIG, \
n) static const struct input_listener_config config_##n = \ n) static const struct input_listener_config config_##n = \
{ \ { \
.listener_index = n, \
.base = IL_EXTRACT_CONFIG(DT_DRV_INST(n), n, base), \ .base = IL_EXTRACT_CONFIG(DT_DRV_INST(n), n, base), \
.layer_overrides_len = (0 DT_INST_FOREACH_CHILD(n, IL_ONE)), \ .layer_overrides_len = (0 DT_INST_FOREACH_CHILD(n, IL_ONE)), \
.layer_overrides = {DT_INST_FOREACH_CHILD_SEP_VARGS(n, IL_OVERRIDE, (, ), n)}, \ .layer_overrides = {DT_INST_FOREACH_CHILD_SEP_VARGS(n, IL_OVERRIDE, (, ), n)}, \

View 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)

View File

@ -25,7 +25,7 @@ static int cm_handle_event(const struct device *dev, struct input_event *event,
const struct cm_config *cfg = dev->config; const struct cm_config *cfg = dev->config;
if (event->type != cfg->type) { if (event->type != cfg->type) {
return 0; return ZMK_INPUT_PROC_CONTINUE;
} }
for (int i = 0; i < cfg->mapping_size / 2; i++) { 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 = { static struct zmk_input_processor_driver_api cm_driver_api = {

View File

@ -47,7 +47,7 @@ static int scaler_handle_event(const struct device *dev, struct input_event *eve
const struct scaler_config *cfg = dev->config; const struct scaler_config *cfg = dev->config;
if (event->type != cfg->type) { if (event->type != cfg->type) {
return 0; return ZMK_INPUT_PROC_CONTINUE;
} }
for (int i = 0; i < cfg->codes_len; i++) { 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 = { static struct zmk_input_processor_driver_api scaler_driver_api = {

View File

@ -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)); 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) { static int temp_layer_init(const struct device *dev) {

View File

@ -42,7 +42,7 @@ static int ipt_handle_event(const struct device *dev, struct input_event *event,
const struct ipt_config *cfg = dev->config; const struct ipt_config *cfg = dev->config;
if (event->type != cfg->type) { if (event->type != cfg->type) {
return 0; return ZMK_INPUT_PROC_CONTINUE;
} }
if (param1 & INPUT_TRANSFORM_XY_SWAP) { 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; event->value = -event->value;
} }
return 0; return ZMK_INPUT_PROC_CONTINUE;
} }
static struct zmk_input_processor_driver_api ipt_driver_api = { static struct zmk_input_processor_driver_api ipt_driver_api = {

View File

@ -0,0 +1,2 @@
s/.*hid_mouse_//p
s/.*hid_listener_keycode_//p

View File

@ -0,0 +1,6 @@
pressed: usage_page 0x07 keycode 0x04 implicit_mods 0x00 explicit_mods 0x00
released: usage_page 0x07 keycode 0x04 implicit_mods 0x00 explicit_mods 0x00
pressed: usage_page 0x07 keycode 0x05 implicit_mods 0x00 explicit_mods 0x00
released: usage_page 0x07 keycode 0x05 implicit_mods 0x00 explicit_mods 0x00
pressed: usage_page 0x07 keycode 0x06 implicit_mods 0x00 explicit_mods 0x00
released: usage_page 0x07 keycode 0x06 implicit_mods 0x00 explicit_mods 0x00

View File

@ -0,0 +1,6 @@
CONFIG_GPIO=n
CONFIG_ZMK_BLE=n
CONFIG_LOG=y
CONFIG_LOG_BACKEND_SHOW_COLOR=n
CONFIG_ZMK_LOG_LEVEL_DBG=y
CONFIG_ZMK_POINTING=y

View File

@ -0,0 +1,44 @@
#include <dt-bindings/zmk/input_transform.h>
#include <zephyr/dt-bindings/input/input-event-codes.h>
#include <behaviors.dtsi>
#include <input/processors.dtsi>
#include <dt-bindings/zmk/keys.h>
#include <dt-bindings/zmk/kscan_mock.h>
#include <dt-bindings/zmk/pointing.h>
&zip_button_behaviors {
bindings = <&kp A &kp B &kp C>;
};
&mkp_input_listener {
input-processors = <&zip_button_behaviors>;
};
/ {
keymap {
compatible = "zmk,keymap";
label ="Default keymap";
default_layer {
bindings = <
&mkp LCLK &mkp RCLK
&mkp MCLK &none
>;
};
};
};
&kscan {
events = <
ZMK_MOCK_PRESS(0,0,10)
ZMK_MOCK_RELEASE(0,0,10)
ZMK_MOCK_PRESS(0,1,10)
ZMK_MOCK_RELEASE(0,1,10)
ZMK_MOCK_PRESS(1,0,10)
ZMK_MOCK_RELEASE(1,0,10)
>;
};

View File

@ -0,0 +1,2 @@
s/.*hid_mouse_//p
s/.*hid_listener_keycode_//p

View File

@ -0,0 +1,16 @@
pressed: usage_page 0x07 keycode 0x04 implicit_mods 0x00 explicit_mods 0x00
released: usage_page 0x07 keycode 0x04 implicit_mods 0x00 explicit_mods 0x00
pressed: usage_page 0x07 keycode 0x05 implicit_mods 0x00 explicit_mods 0x00
released: usage_page 0x07 keycode 0x05 implicit_mods 0x00 explicit_mods 0x00
pressed: usage_page 0x07 keycode 0x06 implicit_mods 0x00 explicit_mods 0x00
released: usage_page 0x07 keycode 0x06 implicit_mods 0x00 explicit_mods 0x00
pressed: usage_page 0x07 keycode 0xE0 implicit_mods 0x00 explicit_mods 0x00
released: usage_page 0x07 keycode 0xE0 implicit_mods 0x00 explicit_mods 0x00
pressed: usage_page 0x07 keycode 0xE1 implicit_mods 0x00 explicit_mods 0x00
released: usage_page 0x07 keycode 0xE1 implicit_mods 0x00 explicit_mods 0x00
pressed: usage_page 0x07 keycode 0xE2 implicit_mods 0x00 explicit_mods 0x00
released: usage_page 0x07 keycode 0xE2 implicit_mods 0x00 explicit_mods 0x00
pressed: usage_page 0x07 keycode 0xE0 implicit_mods 0x00 explicit_mods 0x00
pressed: usage_page 0x07 keycode 0x05 implicit_mods 0x00 explicit_mods 0x00
released: usage_page 0x07 keycode 0xE0 implicit_mods 0x00 explicit_mods 0x00
released: usage_page 0x07 keycode 0x05 implicit_mods 0x00 explicit_mods 0x00

View File

@ -0,0 +1,6 @@
CONFIG_GPIO=n
CONFIG_ZMK_BLE=n
CONFIG_LOG=y
CONFIG_LOG_BACKEND_SHOW_COLOR=n
CONFIG_ZMK_LOG_LEVEL_DBG=y
CONFIG_ZMK_POINTING=y

View File

@ -0,0 +1,54 @@
#include <dt-bindings/zmk/input_transform.h>
#include <zephyr/dt-bindings/input/input-event-codes.h>
#include <behaviors.dtsi>
#include <input/processors.dtsi>
#include <dt-bindings/zmk/keys.h>
#include <dt-bindings/zmk/kscan_mock.h>
#include <dt-bindings/zmk/pointing.h>
&zip_button_behaviors {
bindings = <&mt LCTL A &mt LSHFT B &mt LALT C>;
};
&mkp_input_listener {
input-processors = <&zip_button_behaviors>;
};
/ {
keymap {
compatible = "zmk,keymap";
label ="Default keymap";
default_layer {
bindings = <
&mkp LCLK &mkp RCLK
&mkp MCLK &none
>;
};
};
};
&kscan {
events = <
ZMK_MOCK_PRESS(0,0,10)
ZMK_MOCK_RELEASE(0,0,10)
ZMK_MOCK_PRESS(0,1,10)
ZMK_MOCK_RELEASE(0,1,10)
ZMK_MOCK_PRESS(1,0,10)
ZMK_MOCK_RELEASE(1,0,10)
ZMK_MOCK_PRESS(0,0,500)
ZMK_MOCK_RELEASE(0,0,10)
ZMK_MOCK_PRESS(0,1,500)
ZMK_MOCK_RELEASE(0,1,10)
ZMK_MOCK_PRESS(1,0,500)
ZMK_MOCK_RELEASE(1,0,10)
ZMK_MOCK_PRESS(0,0,10)
ZMK_MOCK_PRESS(0,1,10)
ZMK_MOCK_RELEASE(0,1,10)
ZMK_MOCK_RELEASE(0,0,10)
>;
};

View File

@ -0,0 +1,81 @@
---
title: Behaviors Input Processor
sidebar_label: Behaviors
---
## Overview
The behaviors input processor is used invoke standard behaviors when certain input events occur; most frequently this is used to trigger behaviors when certain mouse buttons are triggered by physical pointing devices.
:::note
This input processor is primarily intended for `INPUT_EV_KEY` type of events that have a binary on/off state, not vector types for relative or absolute movements.
:::
:::note[Source-specific behaviors on split keyboards]
Invoking a [source-specific behavior](../../features/split-keyboards.md#source-locality-behaviors) such as one of the [reset behaviors](../behaviors/reset.md) using this input processor will always trigger it on the central side of the keyboard, regardless of the side includes the input device that originally generated the input event.
:::
## Usage
When used, this input processor takes no parameters, as the event code to behavior mapping is specified in the definition of the specific processor instance, e.g.:
```dts
&zip_button_behaviors
```
## Pre-Defined Instances
One pre-defined instance of the out-of-band behaviors input processor is available:
| Reference | Description |
| ----------------------- | -------------------------------------------------- |
| `&zip_button_behaviors` | Maps left/right/middle clicks to a given behavior. |
Should you wish to update the existing instance to trigger different behaviors for each mouse button, you can override the `bindings` property, e.g.:
```dts
&zip_button_behaviors {
bindings = <&kp A &kp B &kp C>;
};
```
By default, the `bindings` property maps all the buttons to [`&none`](../behaviors/misc.md#none), so you will want to override the `bindings` property like above if you use this processor by assigning it to an [input listener](usage.md).
## User-Defined Instances
Users can define new instances of the out-of-band behaviors input processor if they want to target different codes or assign different behaviors.
### Example
Below example maps the left mouse button code to the middle mouse button.
```dts
#include <zephyr/dt-bindings/input/input-event-codes.h>
/ {
input_processors {
zip_right_click_trigger_paste: zip_right_click_trigger_paste {
compatible = "zmk,input-processor-behaviors";
#input-processor-cells = <0>;
codes = <INPUT_BTN_1>;
bindings = <&kp LC(V) >;
};
};
}
```
### Compatible
The behaviors input processor uses a `compatible` property of `"zmk,input-processor-behaviors"`.
### Standard Properties
- `#input-processor-cells` - required to be constant value of `<0>`.
### User Properties
- `type` - The [type](https://github.com/zmkfirmware/zephyr/blob/v3.5.0%2Bzmk-fixes/include/zephyr/dt-bindings/input/input-event-codes.h#L25) of events to scale. Usually, this is `INPUT_EV_KEY` for key/button events. The default value if omitted is `INPUT_EV_KEY`.
- `codes` - The specific codes of the given type to capture, e.g. [button event codes](https://github.com/zmkfirmware/zephyr/blob/v3.5.0%2Bzmk-fixes/include/zephyr/dt-bindings/input/input-event-codes.h#L180). This list must be the same length as the `bindings` property.
- `bindings` - The bindings to trigger when an event with the corresponding code is processed.

View File

@ -25,25 +25,27 @@ A set of predefined input processors is available by adding the following at the
Once included, you can use the following: Once included, you can use the following:
| Binding | Processor | Description | | Binding | Processor | Description |
| -------------------------- | ----------------------------------------------------------- | ------------------------------------------------------------------- | | -------------------------- | ------------------------------------------------------------ | ------------------------------------------------------------------- |
| `&zip_xy_scaler` | [XY Scaler](scaler.md#pre-defined-instances) | Scale a the X/Y input events using a multiplier and divisor | | `&zip_xy_scaler` | [XY Scaler](scaler.md#pre-defined-instances) | Scale a the X/Y input events using a multiplier and divisor |
| `&zip_x_scaler` | [X Scaler](scaler.md#pre-defined-instances) | Scale a the X input events using a multiplier and divisor | | `&zip_x_scaler` | [X Scaler](scaler.md#pre-defined-instances) | Scale a the X input events using a multiplier and divisor |
| `&zip_y_scaler` | [Y Scaler](scaler.md#pre-defined-instances) | Scale a the Y input events using a multiplier and divisor | | `&zip_y_scaler` | [Y Scaler](scaler.md#pre-defined-instances) | Scale a the Y input events using a multiplier and divisor |
| `&zip_xy_transform` | [XY Transform](transformer.md#pre-defined-instances) | Transform X/Y values, e.g. inverting or swapping | | `&zip_xy_transform` | [XY Transform](transformer.md#pre-defined-instances) | Transform X/Y values, e.g. inverting or swapping |
| `&zip_scroll_transform` | [Scroll Transform](transformer.md#pre-defined-instances) | Transform wheel/horizontal wheel values, e.g. inverting or swapping | | `&zip_scroll_transform` | [Scroll Transform](transformer.md#pre-defined-instances) | Transform wheel/horizontal wheel values, e.g. inverting or swapping |
| `&zip_xy_to_scroll_mapper` | [XY To Scroll Mapper](code-mapper.md#pre-defined-instances) | Map X/Y values to scroll wheel/horizontal wheel events | | `&zip_xy_to_scroll_mapper` | [XY To Scroll Mapper](code-mapper.md#pre-defined-instances) | Map X/Y values to scroll wheel/horizontal wheel events |
| `&zip_xy_swap_mapper` | [XY Swap Mapper](code-mapper.md#pre-defined-instances) | Swap X/Y values | | `&zip_xy_swap_mapper` | [XY Swap Mapper](code-mapper.md#pre-defined-instances) | Swap X/Y values |
| `&zip_temp_layer` | [Temporary Layer](temp-layer.md#pre-defined-instances) | Temporarily enable a layer during pointer use | | `&zip_temp_layer` | [Temporary Layer](temp-layer.md#pre-defined-instances) | Temporarily enable a layer during pointer use |
| `&zip_button_behaviors` | [Mouse Button Behaviors](behaviors.md#pre-defined-instances) | Trigger behaviors when certain mouse buttons are pressed |
### User-Defined Processors ### User-Defined Processors
Several of the input processors that have predefined instances, e.g. `&zip_xy_scaler` or `&zip_xy_to_scroll_mapper` can also have new instances created with custom properties around which input codes to scale, or which codes to map, etc. Several of the input processors that have predefined instances, e.g. `&zip_xy_scaler` or `&zip_xy_to_scroll_mapper` can also have new instances created with custom properties around which input codes to scale, or which codes to map, etc.
| Compatible | Processor | Description | | Compatible | Processor | Description |
| --------------------------------- | ---------------------------------------------------- | ------------------------------------------------ | | --------------------------------- | ---------------------------------------------------- | --------------------------------------------------- |
| `zmk,input-processor-transform` | [Transform](transformer.md#user-defined-instances) | Perform various transforms like inverting values | | `zmk,input-processor-transform` | [Transform](transformer.md#user-defined-instances) | Perform various transforms like inverting values |
| `zmk,input-processor-code-mapper` | [Code Mapper](code-mapper.md#user-defined-instances) | Map one event code to another type | | `zmk,input-processor-code-mapper` | [Code Mapper](code-mapper.md#user-defined-instances) | Map one event code to another type |
| `zmk,input-processor-behaviors` | [Behaviors](behaviors.md#user-defined-instances) | Trigger behaviors for certain matching input events |
## External Processors ## External Processors