Compare commits

..

10 commits

Author SHA1 Message Date
Peter Johanson
671cea5eb0 fix: Various fixes for building.
Some checks failed
pre-commit / pre-commit (push) Waiting to run
BLE Tests / collect-tests (push) Has been cancelled
Tests / collect-tests (push) Has been cancelled
BLE Tests / run-tests (push) Has been cancelled
Tests / run-tests (push) Has been cancelled
2024-09-12 00:03:55 +02:00
Peter Johanson
ca094e61bc feat(mouse): Updated mouse keys docs. 2024-09-12 00:03:55 +02:00
Peter Johanson
4cf0f458b4 fix(mouse): Fix up tests and make ZMK_MOUSE explicit. 2024-09-12 00:03:55 +02:00
Peter Johanson
13ab1cefdc refactor(mouse): Tweak behavior inclusion, listener code
* Always import mouse keys behavior and their associated listeners.
* Tweak listener code to only add listener nodes when
  listener and the associated input device are enabled.
2024-09-12 00:03:52 +02:00
Peter Johanson
5e2e1a9bad fix: Testing fixes for listener refactor. 2024-09-12 00:01:17 +02:00
Peter Johanson
1c0ae5725a chore: Minor input behavior clean-up. 2024-09-12 00:00:32 +02:00
Peter Johanson
326505c4c0 refactor: Proper per-device input listeners.
* Buffer data from input devices and only surface to HID once synd'd.
2024-09-12 00:00:28 +02:00
Peter Johanson
33fc17b4c1 fix(mouse): Fixes for logging.
* Corrected logging for two-axis input timestamps.
2024-09-11 23:59:40 +02:00
Peter Johanson
4d37113f3f refactor(mouse): Remove mouse work queue, Kconfig
* Remove now-unused mouse work queue and related mouse main file.
* Move ticks config into a DTS property on the two axis input behavior.
2024-09-11 23:59:40 +02:00
Peter Johanson
e9513c51d3 feat(mouse): Add input configs for data mods.
* Add ability to swap X/Y, invert X and Y values, and apply a
  scalar multiplier/divisor.
2024-09-11 23:59:40 +02:00
41 changed files with 615 additions and 220 deletions

View file

@ -27,7 +27,6 @@ target_sources(app PRIVATE src/behavior.c)
target_sources_ifdef(CONFIG_ZMK_KSCAN_SIDEBAND_BEHAVIORS app PRIVATE src/kscan_sideband_behaviors.c)
target_sources(app PRIVATE src/matrix_transform.c)
target_sources(app PRIVATE src/physical_layouts.c)
target_sources_ifdef(CONFIG_ZMK_MOUSE app PRIVATE src/mouse/main.c)
target_sources(app PRIVATE src/sensors.c)
target_sources_ifdef(CONFIG_ZMK_WPM app PRIVATE src/wpm.c)
target_sources(app PRIVATE src/event_manager.c)
@ -44,8 +43,7 @@ target_sources_ifdef(CONFIG_ZMK_EXT_POWER app PRIVATE src/behaviors/behavior_ext
target_sources_ifdef(CONFIG_ZMK_BEHAVIOR_SOFT_OFF app PRIVATE src/behaviors/behavior_soft_off.c)
if ((NOT CONFIG_ZMK_SPLIT) OR CONFIG_ZMK_SPLIT_ROLE_CENTRAL)
target_sources(app PRIVATE src/hid.c)
target_sources_ifdef(CONFIG_ZMK_MOUSE app PRIVATE src/mouse/main.c)
target_sources_ifdef(CONFIG_ZMK_MOUSE app PRIVATE src/mouse/hid_input_listener.c)
target_sources_ifdef(CONFIG_ZMK_MOUSE app PRIVATE src/mouse/input_listener.c)
target_sources(app PRIVATE src/behaviors/behavior_key_press.c)
target_sources_ifdef(CONFIG_ZMK_BEHAVIOR_KEY_TOGGLE app PRIVATE src/behaviors/behavior_key_toggle.c)
target_sources_ifdef(CONFIG_ZMK_BEHAVIOR_HOLD_TAP app PRIVATE src/behaviors/behavior_hold_tap.c)

View file

@ -71,8 +71,7 @@ config ZMK_BEHAVIOR_KEY_TOGGLE
config ZMK_BEHAVIOR_MOUSE_KEY_PRESS
bool
default y
depends on DT_HAS_ZMK_BEHAVIOR_MOUSE_KEY_PRESS_ENABLED
imply ZMK_MOUSE
depends on DT_HAS_ZMK_BEHAVIOR_MOUSE_KEY_PRESS_ENABLED && ZMK_MOUSE
config ZMK_BEHAVIOR_STICKY_KEY
bool
@ -97,8 +96,7 @@ config ZMK_BEHAVIOR_SOFT_OFF
config ZMK_BEHAVIOR_INPUT_TWO_AXIS
bool
default y
depends on DT_HAS_ZMK_BEHAVIOR_INPUT_TWO_AXIS_ENABLED
imply ZMK_MOUSE
depends on DT_HAS_ZMK_BEHAVIOR_INPUT_TWO_AXIS_ENABLED && ZMK_MOUSE
config ZMK_BEHAVIOR_SENSOR_ROTATE_COMMON
bool

View file

@ -1,3 +1,9 @@
/*
* Copyright (c) 2024 The ZMK Contributors
*
* SPDX-License-Identifier: MIT
*/
#include <behaviors/key_press.dtsi>
#include <behaviors/key_toggle.dtsi>
#include <behaviors/transparent.dtsi>
@ -19,9 +25,9 @@
#include <behaviors/key_repeat.dtsi>
#include <behaviors/backlight.dtsi>
#include <behaviors/macros.dtsi>
#include <behaviors/mouse_key_press.dtsi>
#include <behaviors/soft_off.dtsi>
#include <behaviors/studio_unlock.dtsi>
#include <behaviors/mouse_move.dtsi>
#include <behaviors/mouse_scroll.dtsi>
#include <behaviors/macros.dtsi>
#include <behaviors/mouse_keys.dtsi>

View file

@ -16,4 +16,9 @@
#binding-cells = <1>;
};
};
mkp_input_listener: mkp_input_listener {
compatible = "zmk,input-listener";
device = <&mkp>;
};
};

View file

@ -0,0 +1,9 @@
/*
* Copyright (c) 2024 The ZMK Contributors
*
* SPDX-License-Identifier: MIT
*/
#include "mouse_key_press.dtsi"
#include "mouse_move.dtsi"
#include "mouse_scroll.dtsi"

View file

@ -1,3 +1,9 @@
/*
* Copyright (c) 2024 The ZMK Contributors
*
* SPDX-License-Identifier: MIT
*/
#include <zephyr/dt-bindings/input/input-event-codes.h>
/ {
@ -11,4 +17,9 @@
acceleration-exponent = <1>;
};
};
mmv_input_listener: mmv_input_listener {
compatible = "zmk,input-listener";
device = <&mmv>;
};
};

View file

@ -1,3 +1,10 @@
/*
* Copyright (c) 2024 The ZMK Contributors
*
* SPDX-License-Identifier: MIT
*/
#include <zephyr/dt-bindings/input/input-event-codes.h>
/ {
@ -11,4 +18,9 @@
acceleration-exponent = <0>;
};
};
msc_input_listener: msc_input_listener {
compatible = "zmk,input-listener";
device = <&msc>;
};
};

View file

@ -11,6 +11,10 @@ properties:
y-input-code:
type: int
required: true
trigger-period-ms:
type: int
default: 16
description: The time (in ms) between generated inputs when an input has non-zero speed.
delay-ms:
type: int
time-to-max-speed-ms:

View file

@ -0,0 +1,21 @@
description: |
Listener to subscribe to input events and send HID updates after processing
compatible: "zmk,input-listener"
properties:
device:
type: phandle
required: true
xy-swap:
type: boolean
x-invert:
type: boolean
y-invert:
type: boolean
scale-multiplier:
type: int
default: 1
scale-divisor:
type: int
default: 1

View file

@ -77,9 +77,9 @@
#define ZMK_HID_REPORT_ID_CONSUMER 0x02
#define ZMK_HID_REPORT_ID_MOUSE 0x03
// Needed until Zephyr offers a 2 byte usage macro
#define HID_USAGE16(idx) \
HID_ITEM(HID_ITEM_TAG_USAGE, HID_ITEM_TYPE_LOCAL, 2), (idx & 0xFF), (idx >> 8 & 0xFF)
#define HID_USAGE16(a, b) HID_ITEM(HID_ITEM_TAG_USAGE, HID_ITEM_TYPE_LOCAL, 2), a, b
#define HID_USAGE16_SINGLE(a) HID_USAGE16((a & 0xFF), ((a >> 8) & 0xFF))
static const uint8_t zmk_hid_report_desc[] = {
HID_USAGE_PAGE(HID_USAGE_GEN_DESKTOP),
@ -195,7 +195,7 @@ static const uint8_t zmk_hid_report_desc[] = {
HID_REPORT_COUNT(0x03),
HID_INPUT(ZMK_HID_MAIN_VAL_DATA | ZMK_HID_MAIN_VAL_VAR | ZMK_HID_MAIN_VAL_REL),
HID_USAGE_PAGE(HID_USAGE_CONSUMER),
HID_USAGE16(HID_USAGE_CONSUMER_AC_PAN),
HID_USAGE16_SINGLE(HID_USAGE_CONSUMER_AC_PAN),
HID_LOGICAL_MIN16(0xFF, -0x7F),
HID_LOGICAL_MAX16(0xFF, 0x7F),
HID_REPORT_SIZE(0x08),

View file

@ -10,5 +10,3 @@
typedef uint8_t zmk_mouse_button_flags_t;
typedef uint16_t zmk_mouse_button_t;
int zmk_mouse_init(void);

View file

@ -43,12 +43,13 @@ struct behavior_input_two_axis_data {
struct behavior_input_two_axis_config {
int16_t x_code;
int16_t y_code;
int delay_ms;
int time_to_max_speed_ms;
uint16_t delay_ms;
uint16_t time_to_max_speed_ms;
uint8_t trigger_period_ms;
// acceleration exponent 0: uniform speed
// acceleration exponent 1: uniform acceleration
// acceleration exponent 2: uniform jerk
int acceleration_exponent;
uint8_t acceleration_exponent;
};
#if CONFIG_MINIMAL_LIBC
@ -107,10 +108,9 @@ static float update_movement_1d(const struct behavior_input_two_axis_config *con
}
int64_t move_duration = ms_since_start(state->start_time, now, config->delay_ms);
move =
(move_duration > 0)
? (speed(config, state->speed, move_duration) * CONFIG_ZMK_MOUSE_TICK_DURATION / 1000)
: 0;
move = (move_duration > 0)
? (speed(config, state->speed, move_duration) * config->trigger_period_ms / 1000)
: 0;
track_remainder(&(move), &(state->remainder));
@ -145,10 +145,10 @@ static void tick_work_cb(struct k_work *work) {
const struct device *dev = data->dev;
const struct behavior_input_two_axis_config *cfg = dev->config;
uint32_t timestamp = k_uptime_get();
uint64_t timestamp = k_uptime_get();
LOG_INF("tick start times: %lld %lld %lld", data->state.x.start_time, data->state.y.start_time,
timestamp);
LOG_INF("x start: %llu, y start: %llu, current timestamp: %llu", data->state.x.start_time,
data->state.y.start_time, timestamp);
struct vector2d move = update_movement_2d(cfg, &data->state, timestamp);
@ -165,33 +165,36 @@ static void tick_work_cb(struct k_work *work) {
}
if (should_be_working(data)) {
k_work_schedule(&data->tick_work, K_MSEC(CONFIG_ZMK_MOUSE_TICK_DURATION));
k_work_schedule(&data->tick_work, K_MSEC(cfg->trigger_period_ms));
}
}
static void set_start_times_for_activity_1d(struct movement_state_1d *state) {
if (state->speed != 0 && state->start_time == 0) {
state->start_time = k_uptime_get();
} else if (state->speed == 0) {
state->start_time = 0;
}
}
static void set_start_times_for_activity(struct movement_state_2d *state) {
if (state->x.speed != 0 && state->x.start_time == 0) {
state->x.start_time = k_uptime_get();
}
if (state->y.speed != 0 && state->y.start_time == 0) {
state->y.start_time = k_uptime_get();
}
set_start_times_for_activity_1d(&state->x);
set_start_times_for_activity_1d(&state->y);
}
static void update_work_scheduling(const struct device *dev) {
struct behavior_input_two_axis_data *data = dev->data;
const struct behavior_input_two_axis_config *cfg = dev->config;
set_start_times_for_activity(&data->state);
if (should_be_working(data)) {
k_work_schedule(&data->tick_work, K_MSEC(CONFIG_ZMK_MOUSE_TICK_DURATION));
k_work_schedule(&data->tick_work, K_MSEC(cfg->trigger_period_ms));
} else {
k_work_cancel_delayable(&data->tick_work);
}
}
int zmk_input_synth_pointer_adjust_speed(const struct device *dev, int16_t dx, int16_t dy) {
int behavior_input_two_axis_adjust_speed(const struct device *dev, int16_t dx, int16_t dy) {
struct behavior_input_two_axis_data *data = dev->data;
LOG_DBG("Adjusting: %d %d", dx, dy);
@ -205,15 +208,6 @@ int zmk_input_synth_pointer_adjust_speed(const struct device *dev, int16_t dx, i
return 0;
}
// static void process_key_state(const struct device *dev, int32_t val, bool pressed) {
// for (int i = 0; i < ZMK_HID_MOUSE_NUM_BUTTONS; i++) {
// if (val & BIT(i)) {
// WRITE_BIT(val, i, 0);
// input_report_key(dev, INPUT_BTN_0 + i, pressed ? 1 : 0, val == 0, K_FOREVER);
// }
// }
// }
static int behavior_input_two_axis_init(const struct device *dev) {
struct behavior_input_two_axis_data *data = dev->data;
@ -233,7 +227,7 @@ static int on_keymap_binding_pressed(struct zmk_behavior_binding *binding,
int16_t x = MOVE_X_DECODE(binding->param1);
int16_t y = MOVE_Y_DECODE(binding->param1);
zmk_input_synth_pointer_adjust_speed(behavior_dev, x, y);
behavior_input_two_axis_adjust_speed(behavior_dev, x, y);
return 0;
}
@ -246,7 +240,7 @@ static int on_keymap_binding_released(struct zmk_behavior_binding *binding,
int16_t x = MOVE_X_DECODE(binding->param1);
int16_t y = MOVE_Y_DECODE(binding->param1);
zmk_input_synth_pointer_adjust_speed(behavior_dev, -x, -y);
behavior_input_two_axis_adjust_speed(behavior_dev, -x, -y);
return 0;
}
@ -258,6 +252,7 @@ static const struct behavior_driver_api behavior_input_two_axis_driver_api = {
static struct behavior_input_two_axis_config behavior_input_two_axis_config_##n = { \
.x_code = DT_INST_PROP(n, x_input_code), \
.y_code = DT_INST_PROP(n, y_input_code), \
.trigger_period_ms = DT_INST_PROP(n, trigger_period_ms), \
.delay_ms = DT_INST_PROP_OR(n, delay_ms, 0), \
.time_to_max_speed_ms = DT_INST_PROP(n, time_to_max_speed_ms), \
.acceleration_exponent = DT_INST_PROP_OR(n, acceleration_exponent, 1), \

View file

@ -30,8 +30,5 @@ int main(void) {
zmk_display_init();
#endif /* CONFIG_ZMK_DISPLAY */
#ifdef CONFIG_ZMK_MOUSE
zmk_mouse_init();
#endif /* CONFIG_ZMK_MOUSE */
return 0;
}

View file

@ -1,40 +1,8 @@
# Copyright (c) 2023 The ZMK Contributors
# SPDX-License-Identifier: MIT
menuconfig ZMK_MOUSE
config ZMK_MOUSE
bool "Mouse Emulation"
select INPUT
select INPUT_THREAD_PRIORITY_OVERRIDE
if ZMK_MOUSE
config ZMK_MOUSE_TICK_DURATION
int "Mouse tick duration in ms"
default 16
choice ZMK_MOUSE_WORK_QUEUE
prompt "Work queue selection for mouse events"
default ZMK_MOUSE_WORK_QUEUE_DEDICATED
config ZMK_MOUSE_WORK_QUEUE_SYSTEM
bool "Use default system work queue for mouse events"
config ZMK_MOUSE_WORK_QUEUE_DEDICATED
bool "Use dedicated work queue for mouse events"
endchoice
if ZMK_MOUSE_WORK_QUEUE_DEDICATED
config ZMK_MOUSE_DEDICATED_THREAD_STACK_SIZE
int "Stack size for dedicated mouse thread/queue"
default 2048
config ZMK_MOUSE_DEDICATED_THREAD_PRIORITY
int "Thread priority for dedicated mouse thread/queue"
default 3
endif # ZMK_MOUSE_WORK_QUEUE_DEDICATED
endif

View file

@ -1,72 +0,0 @@
/*
* Copyright (c) 2020 The ZMK Contributors
*
* SPDX-License-Identifier: MIT
*/
#include <zephyr/kernel.h>
#include <zephyr/input/input.h>
#include <zephyr/dt-bindings/input/input-event-codes.h>
#include <zmk/mouse.h>
#include <zmk/endpoints.h>
#include <zmk/hid.h>
void handle_rel_code(struct input_event *evt) {
switch (evt->code) {
case INPUT_REL_X:
zmk_hid_mouse_movement_update(evt->value, 0);
break;
case INPUT_REL_Y:
zmk_hid_mouse_movement_update(0, evt->value);
break;
case INPUT_REL_WHEEL:
zmk_hid_mouse_scroll_update(0, evt->value);
break;
case INPUT_REL_HWHEEL:
zmk_hid_mouse_scroll_update(evt->value, 0);
break;
default:
break;
}
}
void handle_key_code(struct input_event *evt) {
int8_t btn;
switch (evt->code) {
case INPUT_BTN_0:
case INPUT_BTN_1:
case INPUT_BTN_2:
case INPUT_BTN_3:
case INPUT_BTN_4:
btn = evt->code - INPUT_BTN_0;
if (evt->value > 0) {
zmk_hid_mouse_button_press(btn);
} else {
zmk_hid_mouse_button_release(btn);
}
break;
default:
break;
}
}
void input_handler(struct input_event *evt) {
switch (evt->type) {
case INPUT_EV_REL:
handle_rel_code(evt);
break;
case INPUT_EV_KEY:
handle_key_code(evt);
break;
}
if (evt->sync) {
zmk_endpoints_send_mouse_report();
zmk_hid_mouse_scroll_set(0, 0);
zmk_hid_mouse_movement_set(0, 0);
}
}
INPUT_CALLBACK_DEFINE(NULL, input_handler);

View file

@ -0,0 +1,222 @@
/*
* Copyright (c) 2020 The ZMK Contributors
*
* SPDX-License-Identifier: MIT
*/
#define DT_DRV_COMPAT zmk_input_listener
#include <zephyr/device.h>
#include <zephyr/kernel.h>
#include <zephyr/input/input.h>
#include <zephyr/logging/log.h>
LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL);
#include <zephyr/dt-bindings/input/input-event-codes.h>
#include <zmk/endpoints.h>
#include <zmk/mouse.h>
#include <zmk/hid.h>
#define ONE_IF_DEV_OK(n) \
COND_CODE_1(DT_NODE_HAS_STATUS(DT_INST_PHANDLE(n, device), okay), (1 +), (0 +))
#define VALID_LISTENER_COUNT (DT_INST_FOREACH_STATUS_OKAY(ONE_IF_DEV_OK) 0)
#if VALID_LISTENER_COUNT > 0
enum input_listener_xy_data_mode {
INPUT_LISTENER_XY_DATA_MODE_NONE,
INPUT_LISTENER_XY_DATA_MODE_REL,
INPUT_LISTENER_XY_DATA_MODE_ABS,
};
struct input_listener_xy_data {
enum input_listener_xy_data_mode mode;
int16_t x;
int16_t y;
};
struct input_listener_data {
union {
struct {
struct input_listener_xy_data data;
struct input_listener_xy_data wheel_data;
uint8_t button_set;
uint8_t button_clear;
} mouse;
};
};
struct input_listener_config {
bool xy_swap;
bool x_invert;
bool y_invert;
uint16_t scale_multiplier;
uint16_t scale_divisor;
};
static void handle_rel_code(struct input_listener_data *data, struct input_event *evt) {
switch (evt->code) {
case INPUT_REL_X:
data->mouse.data.mode = INPUT_LISTENER_XY_DATA_MODE_REL;
data->mouse.data.x += evt->value;
break;
case INPUT_REL_Y:
data->mouse.data.mode = INPUT_LISTENER_XY_DATA_MODE_REL;
data->mouse.data.y += evt->value;
break;
case INPUT_REL_WHEEL:
data->mouse.wheel_data.mode = INPUT_LISTENER_XY_DATA_MODE_REL;
data->mouse.wheel_data.y += evt->value;
break;
case INPUT_REL_HWHEEL:
data->mouse.wheel_data.mode = INPUT_LISTENER_XY_DATA_MODE_REL;
data->mouse.wheel_data.x += evt->value;
break;
default:
break;
}
}
static void handle_abs_code(const struct input_listener_config *config,
struct input_listener_data *data, struct input_event *evt) {}
static void handle_key_code(const struct input_listener_config *config,
struct input_listener_data *data, struct input_event *evt) {
int8_t btn;
switch (evt->code) {
case INPUT_BTN_0:
case INPUT_BTN_1:
case INPUT_BTN_2:
case INPUT_BTN_3:
case INPUT_BTN_4:
btn = evt->code - INPUT_BTN_0;
if (evt->value > 0) {
WRITE_BIT(data->mouse.button_set, btn, 1);
} else {
WRITE_BIT(data->mouse.button_clear, btn, 1);
}
break;
default:
break;
}
}
static void swap_xy(struct input_event *evt) {
switch (evt->code) {
case INPUT_REL_X:
evt->code = INPUT_REL_Y;
break;
case INPUT_REL_Y:
evt->code = INPUT_REL_X;
break;
}
}
static inline bool is_x_data(const struct input_event *evt) {
return evt->type == INPUT_EV_REL && evt->code == INPUT_REL_X;
}
static inline bool is_y_data(const struct input_event *evt) {
return evt->type == INPUT_EV_REL && evt->code == INPUT_REL_Y;
}
static void filter_with_input_config(const struct input_listener_config *cfg,
struct input_event *evt) {
if (!evt->dev) {
return;
}
if (cfg->xy_swap) {
swap_xy(evt);
}
if ((cfg->x_invert && is_x_data(evt)) || (cfg->y_invert && is_y_data(evt))) {
evt->value = -(evt->value);
}
evt->value = (int16_t)((evt->value * cfg->scale_multiplier) / cfg->scale_divisor);
}
static void clear_xy_data(struct input_listener_xy_data *data) {
data->x = data->y = 0;
data->mode = INPUT_LISTENER_XY_DATA_MODE_NONE;
}
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, evt);
switch (evt->type) {
case INPUT_EV_REL:
handle_rel_code(data, evt);
break;
case INPUT_EV_ABS:
handle_abs_code(config, data, evt);
break;
case INPUT_EV_KEY:
handle_key_code(config, data, evt);
break;
}
if (evt->sync) {
if (data->mouse.wheel_data.mode == INPUT_LISTENER_XY_DATA_MODE_REL) {
zmk_hid_mouse_scroll_set(data->mouse.wheel_data.x, data->mouse.wheel_data.y);
}
if (data->mouse.data.mode == INPUT_LISTENER_XY_DATA_MODE_REL) {
zmk_hid_mouse_movement_set(data->mouse.data.x, data->mouse.data.y);
}
if (data->mouse.button_set != 0) {
for (int i = 0; i < ZMK_HID_MOUSE_NUM_BUTTONS; i++) {
if ((data->mouse.button_set & BIT(i)) != 0) {
zmk_hid_mouse_button_press(i);
}
}
}
if (data->mouse.button_clear != 0) {
for (int i = 0; i < ZMK_HID_MOUSE_NUM_BUTTONS; i++) {
if ((data->mouse.button_clear & BIT(i)) != 0) {
zmk_hid_mouse_button_release(i);
}
}
}
zmk_endpoints_send_mouse_report();
zmk_hid_mouse_scroll_set(0, 0);
zmk_hid_mouse_movement_set(0, 0);
clear_xy_data(&data->mouse.data);
clear_xy_data(&data->mouse.wheel_data);
data->mouse.button_set = data->mouse.button_clear = 0;
}
}
#endif // VALID_LISTENER_COUNT > 0
#define IL_INST(n) \
COND_CODE_1( \
DT_NODE_HAS_STATUS(DT_INST_PHANDLE(n, device), okay), \
(static const struct input_listener_config config_##n = \
{ \
.xy_swap = DT_INST_PROP(n, xy_swap), \
.x_invert = DT_INST_PROP(n, x_invert), \
.y_invert = DT_INST_PROP(n, y_invert), \
.scale_multiplier = DT_INST_PROP(n, scale_multiplier), \
.scale_divisor = DT_INST_PROP(n, scale_divisor), \
}; \
static struct input_listener_data data_##n = {}; \
void input_handler_##n(struct input_event *evt) { \
input_handler(&config_##n, &data_##n, evt); \
} INPUT_CALLBACK_DEFINE(DEVICE_DT_GET(DT_INST_PHANDLE(n, device)), input_handler_##n);), \
())
DT_INST_FOREACH_STATUS_OKAY(IL_INST)

View file

@ -1,30 +0,0 @@
/*
* Copyright (c) 2020 The ZMK Contributors
*
* SPDX-License-Identifier: MIT
*/
#include <zephyr/kernel.h>
#include <zmk/mouse.h>
#if IS_ENABLED(CONFIG_ZMK_MOUSE_WORK_QUEUE_DEDICATED)
K_THREAD_STACK_DEFINE(mouse_work_stack_area, CONFIG_ZMK_MOUSE_DEDICATED_THREAD_STACK_SIZE);
static struct k_work_q mouse_work_q;
#endif
struct k_work_q *zmk_mouse_work_q() {
#if IS_ENABLED(CONFIG_ZMK_MOUSE_WORK_QUEUE_DEDICATED)
return &mouse_work_q;
#else
return &k_sys_work_q;
#endif
}
int zmk_mouse_init(void) {
#if IS_ENABLED(CONFIG_ZMK_MOUSE_WORK_QUEUE_DEDICATED)
k_work_queue_start(&mouse_work_q, mouse_work_stack_area,
K_THREAD_STACK_SIZEOF(mouse_work_stack_area),
CONFIG_ZMK_MOUSE_DEDICATED_THREAD_PRIORITY, NULL);
#endif
return 0;
}

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_MOUSE=y

View file

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

View file

@ -0,0 +1,18 @@
movement_set: Mouse movement set to -1/0
scroll_set: Mouse scroll set to 0/0
movement_set: Mouse movement set to 0/0
movement_set: Mouse movement set to -3/-3
scroll_set: Mouse scroll set to 0/0
movement_set: Mouse movement set to 0/0
movement_set: Mouse movement set to -3/-3
scroll_set: Mouse scroll set to 0/0
movement_set: Mouse movement set to 0/0
movement_set: Mouse movement set to -5/-3
scroll_set: Mouse scroll set to 0/0
movement_set: Mouse movement set to 0/0
movement_set: Mouse movement set to -5/-5
scroll_set: Mouse scroll set to 0/0
movement_set: Mouse movement set to 0/0
movement_set: Mouse movement set to 0/-5
scroll_set: Mouse scroll set to 0/0
movement_set: Mouse movement set to 0/0

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_MOUSE=y

View file

@ -0,0 +1,33 @@
#include <behaviors.dtsi>
#include <dt-bindings/zmk/keys.h>
#include <dt-bindings/zmk/kscan_mock.h>
#include <dt-bindings/zmk/mouse.h>
&mmv_input_listener {
scale-multiplier = <5>;
scale-divisor = <3>;
};
/ {
keymap {
compatible = "zmk,keymap";
label ="Default keymap";
default_layer {
bindings = <
&mmv MOVE_LEFT &mmv MOVE_UP
&none &none
>;
};
};
};
&kscan {
events = <
ZMK_MOCK_PRESS(0,0,10)
ZMK_MOCK_PRESS(0,1,100)
ZMK_MOCK_RELEASE(0,0,10)
ZMK_MOCK_RELEASE(0,1,10)
>;
};

View file

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

View file

@ -0,0 +1,18 @@
movement_set: Mouse movement set to 1/0
scroll_set: Mouse scroll set to 0/0
movement_set: Mouse movement set to 0/0
movement_set: Mouse movement set to 2/2
scroll_set: Mouse scroll set to 0/0
movement_set: Mouse movement set to 0/0
movement_set: Mouse movement set to 2/2
scroll_set: Mouse scroll set to 0/0
movement_set: Mouse movement set to 0/0
movement_set: Mouse movement set to 3/2
scroll_set: Mouse scroll set to 0/0
movement_set: Mouse movement set to 0/0
movement_set: Mouse movement set to 3/3
scroll_set: Mouse scroll set to 0/0
movement_set: Mouse movement set to 0/0
movement_set: Mouse movement set to 0/3
scroll_set: Mouse scroll set to 0/0
movement_set: Mouse movement set to 0/0

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_MOUSE=y

View file

@ -0,0 +1,33 @@
#include <behaviors.dtsi>
#include <dt-bindings/zmk/keys.h>
#include <dt-bindings/zmk/kscan_mock.h>
#include <dt-bindings/zmk/mouse.h>
&mmv_input_listener {
x-invert;
y-invert;
};
/ {
keymap {
compatible = "zmk,keymap";
label ="Default keymap";
default_layer {
bindings = <
&mmv MOVE_LEFT &mmv MOVE_UP
&none &none
>;
};
};
};
&kscan {
events = <
ZMK_MOCK_PRESS(0,0,10)
ZMK_MOCK_PRESS(0,1,100)
ZMK_MOCK_RELEASE(0,0,10)
ZMK_MOCK_RELEASE(0,1,10)
>;
};

View file

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

View file

@ -0,0 +1,18 @@
movement_set: Mouse movement set to 0/-1
scroll_set: Mouse scroll set to 0/0
movement_set: Mouse movement set to 0/0
movement_set: Mouse movement set to -2/-2
scroll_set: Mouse scroll set to 0/0
movement_set: Mouse movement set to 0/0
movement_set: Mouse movement set to -2/-2
scroll_set: Mouse scroll set to 0/0
movement_set: Mouse movement set to 0/0
movement_set: Mouse movement set to -2/-3
scroll_set: Mouse scroll set to 0/0
movement_set: Mouse movement set to 0/0
movement_set: Mouse movement set to -3/-3
scroll_set: Mouse scroll set to 0/0
movement_set: Mouse movement set to 0/0
movement_set: Mouse movement set to -3/0
scroll_set: Mouse scroll set to 0/0
movement_set: Mouse movement set to 0/0

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_MOUSE=y

View file

@ -0,0 +1,32 @@
#include <behaviors.dtsi>
#include <dt-bindings/zmk/keys.h>
#include <dt-bindings/zmk/kscan_mock.h>
#include <dt-bindings/zmk/mouse.h>
&mmv_input_listener {
xy-swap;
};
/ {
keymap {
compatible = "zmk,keymap";
label ="Default keymap";
default_layer {
bindings = <
&mmv MOVE_LEFT &mmv MOVE_UP
&none &none
>;
};
};
};
&kscan {
events = <
ZMK_MOCK_PRESS(0,0,10)
ZMK_MOCK_PRESS(0,1,100)
ZMK_MOCK_RELEASE(0,0,10)
ZMK_MOCK_RELEASE(0,1,10)
>;
};

View file

@ -1,22 +1,18 @@
movement_update: Mouse movement updated to -1/0
movement_set: Mouse movement set to -1/0
scroll_set: Mouse scroll set to 0/0
movement_set: Mouse movement set to 0/0
movement_update: Mouse movement updated to -2/0
movement_update: Mouse movement updated to -2/-2
movement_set: Mouse movement set to -2/-2
scroll_set: Mouse scroll set to 0/0
movement_set: Mouse movement set to 0/0
movement_update: Mouse movement updated to -2/0
movement_update: Mouse movement updated to -2/-2
movement_set: Mouse movement set to -2/-2
scroll_set: Mouse scroll set to 0/0
movement_set: Mouse movement set to 0/0
movement_update: Mouse movement updated to -3/0
movement_update: Mouse movement updated to -3/-2
movement_set: Mouse movement set to -3/-2
scroll_set: Mouse scroll set to 0/0
movement_set: Mouse movement set to 0/0
movement_update: Mouse movement updated to -3/0
movement_update: Mouse movement updated to -3/-3
movement_set: Mouse movement set to -3/-3
scroll_set: Mouse scroll set to 0/0
movement_set: Mouse movement set to 0/0
movement_update: Mouse movement updated to 0/-3
movement_set: Mouse movement set to 0/-3
scroll_set: Mouse scroll set to 0/0
movement_set: Mouse movement set to 0/0

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_MOUSE=y

View file

@ -1,4 +1,5 @@
#include <behaviors.dtsi>
#include <behaviors/mouse_move.dtsi>
#include <dt-bindings/zmk/keys.h>
#include <dt-bindings/zmk/kscan_mock.h>
#include <dt-bindings/zmk/mouse.h>

View file

@ -1,27 +1,24 @@
movement_update: Mouse movement updated to -1/0
movement_set: Mouse movement set to -1/0
scroll_set: Mouse scroll set to 0/0
movement_set: Mouse movement set to 0/0
movement_update: Mouse movement updated to -2/0
movement_set: Mouse movement set to -2/0
scroll_set: Mouse scroll set to 0/0
movement_set: Mouse movement set to 0/0
movement_update: Mouse movement updated to -2/0
movement_set: Mouse movement set to -2/0
scroll_set: Mouse scroll set to 0/0
movement_set: Mouse movement set to 0/0
movement_update: Mouse movement updated to -3/0
movement_set: Mouse movement set to -3/0
scroll_set: Mouse scroll set to 0/0
movement_set: Mouse movement set to 0/0
movement_update: Mouse movement updated to 3/0
movement_set: Mouse movement set to 1/0
scroll_set: Mouse scroll set to 0/0
movement_set: Mouse movement set to 0/0
movement_update: Mouse movement updated to 5/0
movement_set: Mouse movement set to 2/0
scroll_set: Mouse scroll set to 0/0
movement_set: Mouse movement set to 0/0
movement_update: Mouse movement updated to 5/0
movement_set: Mouse movement set to 2/0
scroll_set: Mouse scroll set to 0/0
movement_set: Mouse movement set to 0/0
movement_update: Mouse movement updated to 6/0
movement_set: Mouse movement set to 3/0
scroll_set: Mouse scroll set to 0/0
movement_set: Mouse movement set to 0/0
movement_update: Mouse movement updated to 6/0
scroll_set: Mouse scroll set to 0/0
movement_set: Mouse movement set to 0/0

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_MOUSE=y

View file

@ -1,4 +1,5 @@
#include <behaviors.dtsi>
#include <behaviors/mouse_move.dtsi>
#include <dt-bindings/zmk/keys.h>
#include <dt-bindings/zmk/kscan_mock.h>
#include <dt-bindings/zmk/mouse.h>

View file

@ -1,27 +1,24 @@
movement_update: Mouse movement updated to 0/-1
movement_set: Mouse movement set to 0/-1
scroll_set: Mouse scroll set to 0/0
movement_set: Mouse movement set to 0/0
movement_update: Mouse movement updated to 0/-2
movement_set: Mouse movement set to 0/-2
scroll_set: Mouse scroll set to 0/0
movement_set: Mouse movement set to 0/0
movement_update: Mouse movement updated to 0/-2
movement_set: Mouse movement set to 0/-2
scroll_set: Mouse scroll set to 0/0
movement_set: Mouse movement set to 0/0
movement_update: Mouse movement updated to 0/-3
movement_set: Mouse movement set to 0/-3
scroll_set: Mouse scroll set to 0/0
movement_set: Mouse movement set to 0/0
movement_update: Mouse movement updated to 0/3
movement_set: Mouse movement set to 0/1
scroll_set: Mouse scroll set to 0/0
movement_set: Mouse movement set to 0/0
movement_update: Mouse movement updated to 0/5
movement_set: Mouse movement set to 0/2
scroll_set: Mouse scroll set to 0/0
movement_set: Mouse movement set to 0/0
movement_update: Mouse movement updated to 0/5
movement_set: Mouse movement set to 0/2
scroll_set: Mouse scroll set to 0/0
movement_set: Mouse movement set to 0/0
movement_update: Mouse movement updated to 0/6
scroll_set: Mouse scroll set to 0/0
movement_set: Mouse movement set to 0/0
movement_update: Mouse movement updated to 0/6
movement_set: Mouse movement set to 0/3
scroll_set: Mouse scroll set to 0/0
movement_set: Mouse movement set to 0/0

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_MOUSE=y

View file

@ -1,4 +1,5 @@
#include <behaviors.dtsi>
#include <behaviors/mouse_move.dtsi>
#include <dt-bindings/zmk/keys.h>
#include <dt-bindings/zmk/kscan_mock.h>
#include <dt-bindings/zmk/mouse.h>

View file

@ -43,6 +43,8 @@ Below is a summary of pre-defined behavior bindings and user-definable behaviors
| Binding | Behavior | Description |
| ------- | ----------------------------------------------------------- | ------------------------------- |
| `&mkp` | [Mouse Button Press](mouse-emulation.md#mouse-button-press) | Emulates pressing mouse buttons |
| `&mmv` | [Mouse Button Press](mouse-emulation.md#mouse-move) | Emulates mouse movement |
| `&msc` | [Mouse Button Press](mouse-emulation.md#mouse-scroll) | Emulates mouse scrolling |
## Reset Behaviors

View file

@ -5,8 +5,7 @@ sidebar_label: Mouse Emulation
## Summary
Mouse emulation behaviors send mouse events. Currently, only mouse button presses are supported, but movement
and scroll action support is planned for the future.
Mouse emulation behaviors send mouse events, including mouse button presses, cursor movement and scrolling.
:::warning[Refreshing the HID descriptor]
@ -17,14 +16,12 @@ The mouse functionality will not work over BLE until that is done.
## Configuration Option
This feature can be enabled or disabled explicitly via a config option:
To use any of the behaviors documented here, the ZMK mouse feature must be enabled explicitly via a config option:
```
CONFIG_ZMK_MOUSE=y
```
If you use the mouse key press behavior in your keymap, the feature will automatically be enabled for you.
## Mouse Button Defines
To make it easier to encode the HID mouse button numeric values, include
@ -69,3 +66,67 @@ This example will send press of the fourth mouse button when the binding is trig
```
&mkp MB4
```
## Mouse Move
This behavior sends mouse X/Y movement events to the connected host.
### Behavior Binding
- Reference: `&mmv`
- Parameter: A `uint32` with 16-bits each used for vertical and horizontal velocity.
The following defines can be passed for the parameter:
| Define | Action |
| :----------- | :--------- |
| `MOVE_UP` | Move up |
| `MOVE_DOWN` | Move down |
| `MOVE_LEFT` | Move left |
| `MOVE_RIGHT` | Move right |
### Examples
The following will send a scroll down event to the host when pressed/held:
```
&mmv MOVE_DOWN
```
The following will send a scroll left event to the host when pressed/held:
```
&mmv MOVE_LEFT
```
## Mouse Scroll
This behavior sends vertical and horizontal scroll events to the connected host.
### Behavior Binding
- Reference: `&msc`
- Parameter: A `uint32` with 16-bits each used for vertical and horizontal velocity.
The following defines can be passed for the parameter:
| Define | Action |
| :----------- | :--------- |
| `MOVE_UP` | Move up |
| `MOVE_DOWN` | Move down |
| `MOVE_LEFT` | Move left |
| `MOVE_RIGHT` | Move right |
### Examples
The following will send a scroll down event to the host when pressed/held:
```
&msc MOVE_DOWN
```
The following will send a scroll left event to the host when pressed/held:
```
&msc MOVE_LEFT
```