diff options
author | Pete Johanson <[email protected]> | 2024-12-17 18:50:06 -0700 |
---|---|---|
committer | GitHub <[email protected]> | 2024-12-17 20:50:06 -0500 |
commit | cb867f92dbe4e32675c2137fc6aa914a44ecc8dc (patch) | |
tree | 84ed7a733e5de6d00b3129d3c76f917e250f7095 | |
parent | d0016b34f88b76bd0c1fb5964bbae9d1916dcf76 (diff) | |
download | zmk-cb867f92dbe4e32675c2137fc6aa914a44ecc8dc.tar.gz zmk-cb867f92dbe4e32675c2137fc6aa914a44ecc8dc.zip |
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 <[email protected]>
Co-authored-by: Cem Aksoylar <[email protected]>
25 files changed, 456 insertions, 37 deletions
diff --git a/app/dts/bindings/input_processors/zmk,input-processor-behaviors.yaml b/app/dts/bindings/input_processors/zmk,input-processor-behaviors.yaml new file mode 100644 index 0000000000..c8c54c557a --- /dev/null +++ b/app/dts/bindings/input_processors/zmk,input-processor-behaviors.yaml @@ -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 diff --git a/app/dts/input/processors.dtsi b/app/dts/input/processors.dtsi index d072c0fcfc..17398bada7 100644 --- a/app/dts/input/processors.dtsi +++ b/app/dts/input/processors.dtsi @@ -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>
\ No newline at end of file +#include <input/processors/temp_layer.dtsi> +#include <input/processors/behaviors.dtsi>
\ No newline at end of file diff --git a/app/dts/input/processors/behaviors.dtsi b/app/dts/input/processors/behaviors.dtsi new file mode 100644 index 0000000000..a50f9384b0 --- /dev/null +++ b/app/dts/input/processors/behaviors.dtsi @@ -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>; + }; +};
\ No newline at end of file diff --git a/app/include/drivers/input_processor.h b/app/include/drivers/input_processor.h index aea57476ae..fac4459b91 100644 --- a/app/include/drivers/input_processor.h +++ b/app/include/drivers/input_processor.h @@ -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; @@ -33,6 +36,7 @@ struct zmk_input_processor_entry { } struct zmk_input_processor_state { + uint8_t input_device_index; int16_t *remainder; }; diff --git a/app/include/zmk/combos.h b/app/include/zmk/combos.h new file mode 100644 index 0000000000..a7166dff58 --- /dev/null +++ b/app/include/zmk/combos.h @@ -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))
\ No newline at end of file diff --git a/app/include/zmk/input_listeners.h b/app/include/zmk/input_listeners.h new file mode 100644 index 0000000000..446706fb31 --- /dev/null +++ b/app/include/zmk/input_listeners.h @@ -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)
\ No newline at end of file diff --git a/app/include/zmk/virtual_key_position.h b/app/include/zmk/virtual_key_position.h index 563f951eba..466ea3efc1 100644 --- a/app/include/zmk/virtual_key_position.h +++ b/app/include/zmk/virtual_key_position.h @@ -7,6 +7,8 @@ #pragma once #include <zmk/matrix.h> +#include <zmk/combos.h> +#include <zmk/input_listeners.h> #include <zmk/sensors.h> /** @@ -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)) diff --git a/app/src/pointing/CMakeLists.txt b/app/src/pointing/CMakeLists.txt index ad74c1ea78..8609277d6e 100644 --- a/app/src/pointing/CMakeLists.txt +++ b/app/src/pointing/CMakeLists.txt @@ -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) diff --git a/app/src/pointing/Kconfig b/app/src/pointing/Kconfig index 42cf8e8c07..b4051e1fce 100644 --- a/app/src/pointing/Kconfig +++ b/app/src/pointing/Kconfig @@ -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 diff --git a/app/src/pointing/input_listener.c b/app/src/pointing/input_listener.c index 97e39285d3..28f2530003 100644 --- a/app/src/pointing/input_listener.c +++ b/app/src/pointing/input_listener.c @@ -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}; - - 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++) { @@ -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)}, \ diff --git a/app/src/pointing/input_processor_behaviors.c b/app/src/pointing/input_processor_behaviors.c new file mode 100644 index 0000000000..0c8d75cdc3 --- /dev/null +++ b/app/src/pointing/input_processor_behaviors.c @@ -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)
\ No newline at end of file diff --git a/app/src/pointing/input_processor_code_mapper.c b/app/src/pointing/input_processor_code_mapper.c index 20b5744ea9..b42d3478e8 100644 --- a/app/src/pointing/input_processor_code_mapper.c +++ b/app/src/pointing/input_processor_code_mapper.c @@ -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 = { diff --git a/app/src/pointing/input_processor_scaler.c b/app/src/pointing/input_processor_scaler.c index eb006cd254..c7e08ecdb0 100644 --- a/app/src/pointing/input_processor_scaler.c +++ b/app/src/pointing/input_processor_scaler.c @@ -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 = { diff --git a/app/src/pointing/input_processor_temp_layer.c b/app/src/pointing/input_processor_temp_layer.c index 85a394a693..b106527115 100644 --- a/app/src/pointing/input_processor_temp_layer.c +++ b/app/src/pointing/input_processor_temp_layer.c @@ -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) { diff --git a/app/src/pointing/input_processor_transform.c b/app/src/pointing/input_processor_transform.c index 10828de3f3..c214baee26 100644 --- a/app/src/pointing/input_processor_transform.c +++ b/app/src/pointing/input_processor_transform.c @@ -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 = { diff --git a/app/tests/pointing/mouse-move/processors/behaviors_basic/events.patterns b/app/tests/pointing/mouse-move/processors/behaviors_basic/events.patterns new file mode 100644 index 0000000000..7374badf0f --- /dev/null +++ b/app/tests/pointing/mouse-move/processors/behaviors_basic/events.patterns @@ -0,0 +1,2 @@ +s/.*hid_mouse_//p +s/.*hid_listener_keycode_//p
\ No newline at end of file diff --git a/app/tests/pointing/mouse-move/processors/behaviors_basic/keycode_events.snapshot b/app/tests/pointing/mouse-move/processors/behaviors_basic/keycode_events.snapshot new file mode 100644 index 0000000000..7ad856db60 --- /dev/null +++ b/app/tests/pointing/mouse-move/processors/behaviors_basic/keycode_events.snapshot @@ -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 diff --git a/app/tests/pointing/mouse-move/processors/behaviors_basic/native_posix_64.conf b/app/tests/pointing/mouse-move/processors/behaviors_basic/native_posix_64.conf new file mode 100644 index 0000000000..fa514727ba --- /dev/null +++ b/app/tests/pointing/mouse-move/processors/behaviors_basic/native_posix_64.conf @@ -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 diff --git a/app/tests/pointing/mouse-move/processors/behaviors_basic/native_posix_64.keymap b/app/tests/pointing/mouse-move/processors/behaviors_basic/native_posix_64.keymap new file mode 100644 index 0000000000..e5079d331a --- /dev/null +++ b/app/tests/pointing/mouse-move/processors/behaviors_basic/native_posix_64.keymap @@ -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) + >; +};
\ No newline at end of file diff --git a/app/tests/pointing/mouse-move/processors/behaviors_hold_tap/events.patterns b/app/tests/pointing/mouse-move/processors/behaviors_hold_tap/events.patterns new file mode 100644 index 0000000000..7374badf0f --- /dev/null +++ b/app/tests/pointing/mouse-move/processors/behaviors_hold_tap/events.patterns @@ -0,0 +1,2 @@ +s/.*hid_mouse_//p +s/.*hid_listener_keycode_//p
\ No newline at end of file diff --git a/app/tests/pointing/mouse-move/processors/behaviors_hold_tap/keycode_events.snapshot b/app/tests/pointing/mouse-move/processors/behaviors_hold_tap/keycode_events.snapshot new file mode 100644 index 0000000000..0712fa120c --- /dev/null +++ b/app/tests/pointing/mouse-move/processors/behaviors_hold_tap/keycode_events.snapshot @@ -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 diff --git a/app/tests/pointing/mouse-move/processors/behaviors_hold_tap/native_posix_64.conf b/app/tests/pointing/mouse-move/processors/behaviors_hold_tap/native_posix_64.conf new file mode 100644 index 0000000000..fa514727ba --- /dev/null +++ b/app/tests/pointing/mouse-move/processors/behaviors_hold_tap/native_posix_64.conf @@ -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 diff --git a/app/tests/pointing/mouse-move/processors/behaviors_hold_tap/native_posix_64.keymap b/app/tests/pointing/mouse-move/processors/behaviors_hold_tap/native_posix_64.keymap new file mode 100644 index 0000000000..eb1d8f0279 --- /dev/null +++ b/app/tests/pointing/mouse-move/processors/behaviors_hold_tap/native_posix_64.keymap @@ -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) + >; +};
\ No newline at end of file diff --git a/docs/docs/keymaps/input-processors/behaviors.md b/docs/docs/keymaps/input-processors/behaviors.md new file mode 100644 index 0000000000..a6244ed377 --- /dev/null +++ b/docs/docs/keymaps/input-processors/behaviors.md @@ -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. diff --git a/docs/docs/keymaps/input-processors/index.md b/docs/docs/keymaps/input-processors/index.md index 489003b830..c854b5369e 100644 --- a/docs/docs/keymaps/input-processors/index.md +++ b/docs/docs/keymaps/input-processors/index.md @@ -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: -| 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_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_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_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_temp_layer` | [Temporary Layer](temp-layer.md#pre-defined-instances) | Temporarily enable a layer during pointer use | +| 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_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_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_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_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 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 | -| --------------------------------- | ---------------------------------------------------- | ------------------------------------------------ | -| `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 | +| Compatible | Processor | Description | +| --------------------------------- | ---------------------------------------------------- | --------------------------------------------------- | +| `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-behaviors` | [Behaviors](behaviors.md#user-defined-instances) | Trigger behaviors for certain matching input events | ## External Processors |