From 35eef63fa9a9813a73cedb4c01cd67f50c63d1d1 Mon Sep 17 00:00:00 2001 From: Okke Formsma Date: Fri, 14 May 2021 20:32:14 +0200 Subject: [PATCH] feat(mouse keys): add events, smoothing and acceleration --- app/CMakeLists.txt | 9 +- app/dts/behaviors.dtsi | 2 +- app/dts/behaviors/mouse_move.dtsi | 3 + app/dts/behaviors/mouse_scroll.dtsi | 12 ++ app/dts/behaviors/mouse_wheel.dtsi | 9 -- .../behaviors/zmk,behavior-mouse-move.yaml | 8 ++ .../behaviors/zmk,behavior-mouse-scroll.yaml | 13 ++ .../behaviors/zmk,behavior-mouse-wheel.yaml | 5 - app/include/dt-bindings/zmk/mouse.h | 42 +++--- .../zmk/events/mouse_button_state_changed.h | 27 ++++ .../zmk/events/mouse_move_state_changed.h | 33 +++++ .../zmk/events/mouse_scroll_state_changed.h | 34 +++++ app/include/zmk/events/mouse_state_changed.h | 30 ---- app/include/zmk/events/mouse_tick.h | 36 +++++ app/include/zmk/hid.h | 16 +-- app/include/zmk/mouse.h | 14 ++ app/src/behaviors/behavior_mouse_key_press.c | 19 +-- app/src/behaviors/behavior_mouse_move.c | 33 +++-- app/src/behaviors/behavior_mouse_scroll.c | 58 ++++++++ app/src/behaviors/behavior_mouse_wheel.c | 52 ------- app/src/endpoints.c | 2 +- app/src/events/mouse_button_state_changed.c | 10 ++ app/src/events/mouse_move_state_changed.c | 10 ++ app/src/events/mouse_scroll_state_changed.c | 10 ++ .../{mouse_state_changed.c => mouse_tick.c} | 4 +- app/src/hid.c | 61 +++----- app/src/hid_listener.c | 70 +-------- app/src/mouse/key_listener.c | 135 ++++++++++++++++++ app/src/mouse/tick_listener.c | 129 +++++++++++++++++ app/tests/mouse-keys/mmv/events.patterns | 1 + .../mouse-keys/mmv/keycode_events.snapshot | 2 + app/tests/mouse-keys/mmv/native_posix.keymap | 26 ++++ docs/docs/behaviors/mouse-emulation.md | 6 +- 33 files changed, 653 insertions(+), 268 deletions(-) create mode 100644 app/dts/behaviors/mouse_scroll.dtsi delete mode 100644 app/dts/behaviors/mouse_wheel.dtsi create mode 100644 app/dts/bindings/behaviors/zmk,behavior-mouse-scroll.yaml delete mode 100644 app/dts/bindings/behaviors/zmk,behavior-mouse-wheel.yaml create mode 100644 app/include/zmk/events/mouse_button_state_changed.h create mode 100644 app/include/zmk/events/mouse_move_state_changed.h create mode 100644 app/include/zmk/events/mouse_scroll_state_changed.h delete mode 100644 app/include/zmk/events/mouse_state_changed.h create mode 100644 app/include/zmk/events/mouse_tick.h create mode 100644 app/src/behaviors/behavior_mouse_scroll.c delete mode 100644 app/src/behaviors/behavior_mouse_wheel.c create mode 100644 app/src/events/mouse_button_state_changed.c create mode 100644 app/src/events/mouse_move_state_changed.c create mode 100644 app/src/events/mouse_scroll_state_changed.c rename app/src/events/{mouse_state_changed.c => mouse_tick.c} (55%) create mode 100644 app/src/mouse/key_listener.c create mode 100644 app/src/mouse/tick_listener.c create mode 100644 app/tests/mouse-keys/mmv/events.patterns create mode 100644 app/tests/mouse-keys/mmv/keycode_events.snapshot create mode 100644 app/tests/mouse-keys/mmv/native_posix.keymap diff --git a/app/CMakeLists.txt b/app/CMakeLists.txt index e7c55330..b08fd637 100644 --- a/app/CMakeLists.txt +++ b/app/CMakeLists.txt @@ -27,6 +27,8 @@ target_sources(app PRIVATE src/activity.c) target_sources(app PRIVATE src/kscan.c) target_sources(app PRIVATE src/matrix_transform.c) target_sources(app PRIVATE src/hid.c) +target_sources(app PRIVATE src/mouse/key_listener.c) +target_sources(app PRIVATE src/mouse/tick_listener.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) @@ -38,7 +40,10 @@ target_sources(app PRIVATE src/events/keycode_state_changed.c) target_sources(app PRIVATE src/events/modifiers_state_changed.c) target_sources(app PRIVATE src/events/endpoint_selection_changed.c) target_sources(app PRIVATE src/events/sensor_event.c) -target_sources(app PRIVATE src/events/mouse_state_changed.c) +target_sources(app PRIVATE src/events/mouse_button_state_changed.c) +target_sources(app PRIVATE src/events/mouse_move_state_changed.c) +target_sources(app PRIVATE src/events/mouse_tick.c) +target_sources(app PRIVATE src/events/mouse_scroll_state_changed.c) target_sources_ifdef(CONFIG_ZMK_WPM app PRIVATE src/events/wpm_state_changed.c) target_sources_ifdef(CONFIG_ZMK_BLE app PRIVATE src/events/ble_active_profile_changed.c) target_sources_ifdef(CONFIG_ZMK_BLE app PRIVATE src/events/battery_state_changed.c) @@ -58,7 +63,7 @@ if ((NOT CONFIG_ZMK_SPLIT) OR CONFIG_ZMK_SPLIT_BLE_ROLE_CENTRAL) target_sources(app PRIVATE src/behaviors/behavior_sensor_rotate_key_press.c) target_sources(app PRIVATE src/behaviors/behavior_mouse_key_press.c) target_sources(app PRIVATE src/behaviors/behavior_mouse_move.c) - target_sources(app PRIVATE src/behaviors/behavior_mouse_wheel.c) + target_sources(app PRIVATE src/behaviors/behavior_mouse_scroll.c) target_sources_ifdef(CONFIG_ZMK_EXT_POWER app PRIVATE src/behaviors/behavior_ext_power.c) target_sources(app PRIVATE src/combo.c) target_sources(app PRIVATE src/keymap.c) diff --git a/app/dts/behaviors.dtsi b/app/dts/behaviors.dtsi index 9aa5bf57..3edaa1c1 100644 --- a/app/dts/behaviors.dtsi +++ b/app/dts/behaviors.dtsi @@ -16,4 +16,4 @@ #include #include #include -#include +#include diff --git a/app/dts/behaviors/mouse_move.dtsi b/app/dts/behaviors/mouse_move.dtsi index e442c501..858c5617 100644 --- a/app/dts/behaviors/mouse_move.dtsi +++ b/app/dts/behaviors/mouse_move.dtsi @@ -4,6 +4,9 @@ compatible = "zmk,behavior-mouse-move"; label = "MOUSE_MOVE"; #binding-cells = <1>; + delay-ms = <100>; + time-to-max-speed-ms = <1000>; + acceleration-exponent = <2000>; }; }; }; diff --git a/app/dts/behaviors/mouse_scroll.dtsi b/app/dts/behaviors/mouse_scroll.dtsi new file mode 100644 index 00000000..931124ce --- /dev/null +++ b/app/dts/behaviors/mouse_scroll.dtsi @@ -0,0 +1,12 @@ +/ { + behaviors { + /omit-if-no-ref/ mwh: msc: behavior_mouse_scroll { + compatible = "zmk,behavior-mouse-scroll"; + label = "MOUSE_SCROLL"; + #binding-cells = <1>; + delay-ms = <0>; + time-to-max-speed-ms = <1000>; + acceleration-exponent = <0>; + }; + }; +}; diff --git a/app/dts/behaviors/mouse_wheel.dtsi b/app/dts/behaviors/mouse_wheel.dtsi deleted file mode 100644 index 2a7527ab..00000000 --- a/app/dts/behaviors/mouse_wheel.dtsi +++ /dev/null @@ -1,9 +0,0 @@ -/ { - behaviors { - /omit-if-no-ref/ mwh: behavior_mouse_wheel { - compatible = "zmk,behavior-mouse-wheel"; - label = "MOUSE_WHEEL"; - #binding-cells = <1>; - }; - }; -}; diff --git a/app/dts/bindings/behaviors/zmk,behavior-mouse-move.yaml b/app/dts/bindings/behaviors/zmk,behavior-mouse-move.yaml index f18d78a7..73ec34ec 100644 --- a/app/dts/bindings/behaviors/zmk,behavior-mouse-move.yaml +++ b/app/dts/bindings/behaviors/zmk,behavior-mouse-move.yaml @@ -3,3 +3,11 @@ description: Mouse move compatible: "zmk,behavior-mouse-move" include: one_param.yaml + +properties: + delay-ms: + type: int + time-to-max-speed-ms: + type: int + acceleration-exponent: + type: int diff --git a/app/dts/bindings/behaviors/zmk,behavior-mouse-scroll.yaml b/app/dts/bindings/behaviors/zmk,behavior-mouse-scroll.yaml new file mode 100644 index 00000000..5a932bc5 --- /dev/null +++ b/app/dts/bindings/behaviors/zmk,behavior-mouse-scroll.yaml @@ -0,0 +1,13 @@ +description: Mouse scroll + +compatible: "zmk,behavior-mouse-scroll" + +include: one_param.yaml + +properties: + delay-ms: + type: int + time-to-max-speed-ms: + type: int + acceleration-exponent: + type: int diff --git a/app/dts/bindings/behaviors/zmk,behavior-mouse-wheel.yaml b/app/dts/bindings/behaviors/zmk,behavior-mouse-wheel.yaml deleted file mode 100644 index 89021172..00000000 --- a/app/dts/bindings/behaviors/zmk,behavior-mouse-wheel.yaml +++ /dev/null @@ -1,5 +0,0 @@ -description: Mouse wheel - -compatible: "zmk,behavior-mouse-wheel" - -include: one_param.yaml diff --git a/app/include/dt-bindings/zmk/mouse.h b/app/include/dt-bindings/zmk/mouse.h index 418afc28..cf0415c9 100644 --- a/app/include/dt-bindings/zmk/mouse.h +++ b/app/include/dt-bindings/zmk/mouse.h @@ -29,35 +29,27 @@ #define MB8 (0x80) /* Mouse move behavior */ - -#define MOVE_UP MOVE_VERT(1) - -#define MOVE_DOWN MOVE_VERT(-1) - -#define MOVE_LEFT MOVE_HOR(-1) - -#define MOVE_RIGHT MOVE_HOR(1) - -/* -32767 to 32767, barely usable beyond about 50 (probably depends on screen resolution) */ -#define MOVE_VERT(vert) ((-(vert)) & 0xFFFF) - +#define MOVE_VERT(vert) ((vert)&0xFFFF) +#define MOVE_VERT_DECODE(encoded) (int16_t)((encoded)&0x0000FFFF) #define MOVE_HOR(hor) (((hor)&0xFFFF) << 16) +#define MOVE_HOR_DECODE(encoded) (int16_t)(((encoded)&0xFFFF0000) >> 16) #define MOVE(hor, vert) (MOVE_HOR(hor) + MOVE_VERT(vert)) -/* Mouse wheel behavior */ +#define MOVE_UP MOVE_VERT(-600) +#define MOVE_DOWN MOVE_VERT(600) +#define MOVE_LEFT MOVE_HOR(-600) +#define MOVE_RIGHT MOVE_HOR(600) -#define WHEEL_UP WHEEL_VERT(1) +/* Mouse scroll behavior */ +#define SCROLL_VERT(vert) ((vert)&0xFFFF) +#define SCROLL_VERT_DECODE(encoded) (int16_t)((encoded)&0x0000FFFF) +#define SCROLL_HOR(hor) (((hor)&0xFFFF) << 16) +#define SCROLL_HOR_DECODE(encoded) (int16_t)(((encoded)&0xFFFF0000) >> 16) -#define WHEEL_DOWN WHEEL_VERT(-1) +#define SCROLL(hor, vert) (SCROLL_HOR(hor) + SCROLL_VERT(vert)) -#define WHEEL_LEFT WHEEL_HOR(-1) - -#define WHEEL_RIGHT WHEEL_HOR(1) - -/* -127 to 127, barely usable beyond about 10 */ -#define WHEEL_VERT(vert) ((vert)&0xFF) - -#define WHEEL_HOR(hor) (((hor)&0xFF) << 8) - -#define WHEEL(hor, vert) (WHEEL_HOR(hor) + WHEEL_VERT(vert)) +#define SCROLL_UP SCROLL_VERT(10) +#define SCROLL_DOWN SCROLL_VERT(-10) +#define SCROLL_LEFT SCROLL_HOR(-10) +#define SCROLL_RIGHT SCROLL_HOR(10) diff --git a/app/include/zmk/events/mouse_button_state_changed.h b/app/include/zmk/events/mouse_button_state_changed.h new file mode 100644 index 00000000..7ec4d208 --- /dev/null +++ b/app/include/zmk/events/mouse_button_state_changed.h @@ -0,0 +1,27 @@ + +/* + * Copyright (c) 2020 The ZMK Contributors + * + * SPDX-License-Identifier: MIT + */ + +#pragma once + +#include +#include +#include +#include + +struct zmk_mouse_button_state_changed { + zmk_mouse_button_t buttons; + bool state; + int64_t timestamp; +}; + +ZMK_EVENT_DECLARE(zmk_mouse_button_state_changed); + +static inline struct zmk_mouse_button_state_changed_event * +zmk_mouse_button_state_changed_from_encoded(uint32_t encoded, bool pressed, int64_t timestamp) { + return new_zmk_mouse_button_state_changed((struct zmk_mouse_button_state_changed){ + .buttons = HID_USAGE_ID(encoded), .state = pressed, .timestamp = timestamp}); +} diff --git a/app/include/zmk/events/mouse_move_state_changed.h b/app/include/zmk/events/mouse_move_state_changed.h new file mode 100644 index 00000000..8866f81d --- /dev/null +++ b/app/include/zmk/events/mouse_move_state_changed.h @@ -0,0 +1,33 @@ + +/* + * Copyright (c) 2020 The ZMK Contributors + * + * SPDX-License-Identifier: MIT + */ + +#pragma once + +#include +#include +#include + +struct zmk_mouse_move_state_changed { + struct vector2d max_speed; + struct mouse_config config; + bool state; + int64_t timestamp; +}; + +ZMK_EVENT_DECLARE(zmk_mouse_move_state_changed); + +static inline struct zmk_mouse_move_state_changed_event * +zmk_mouse_move_state_changed_from_encoded(uint32_t encoded, struct mouse_config config, + bool pressed, int64_t timestamp) { + struct vector2d max_speed = (struct vector2d){ + .x = MOVE_HOR_DECODE(encoded), + .y = MOVE_VERT_DECODE(encoded), + }; + + return new_zmk_mouse_move_state_changed((struct zmk_mouse_move_state_changed){ + .max_speed = max_speed, .config = config, .state = pressed, .timestamp = timestamp}); +} diff --git a/app/include/zmk/events/mouse_scroll_state_changed.h b/app/include/zmk/events/mouse_scroll_state_changed.h new file mode 100644 index 00000000..fa60e8a7 --- /dev/null +++ b/app/include/zmk/events/mouse_scroll_state_changed.h @@ -0,0 +1,34 @@ + +/* + * Copyright (c) 2020 The ZMK Contributors + * + * SPDX-License-Identifier: MIT + */ + +#pragma once + +#include +#include +#include +#include + +struct zmk_mouse_scroll_state_changed { + struct vector2d max_speed; + struct mouse_config config; + bool state; + int64_t timestamp; +}; + +ZMK_EVENT_DECLARE(zmk_mouse_scroll_state_changed); + +static inline struct zmk_mouse_scroll_state_changed_event * +zmk_mouse_scroll_state_changed_from_encoded(uint32_t encoded, struct mouse_config config, + bool pressed, int64_t timestamp) { + struct vector2d max_speed = (struct vector2d){ + .x = SCROLL_HOR_DECODE(encoded), + .y = SCROLL_VERT_DECODE(encoded), + }; + + return new_zmk_mouse_scroll_state_changed((struct zmk_mouse_scroll_state_changed){ + .max_speed = max_speed, .config = config, .state = pressed, .timestamp = timestamp}); +} diff --git a/app/include/zmk/events/mouse_state_changed.h b/app/include/zmk/events/mouse_state_changed.h deleted file mode 100644 index d19d091a..00000000 --- a/app/include/zmk/events/mouse_state_changed.h +++ /dev/null @@ -1,30 +0,0 @@ - -/* - * Copyright (c) 2020 The ZMK Contributors - * - * SPDX-License-Identifier: MIT - */ - -#pragma once - -#include -#include - -struct zmk_mouse_state_changed { - int32_t x; - int32_t y; - bool state; - int64_t timestamp; -}; - -ZMK_EVENT_DECLARE(zmk_mouse_state_changed); - -static inline struct zmk_mouse_state_changed_event * -zmk_mouse_state_changed_from_encoded(uint32_t encoded, bool pressed, int64_t timestamp) { - - int32_t x = (encoded & 0xFFFF0000) >> 16; - int32_t y = encoded & 0x0000FFFF; - - return new_zmk_mouse_state_changed( - (struct zmk_mouse_state_changed){.x = x, .y = y, .state = pressed, .timestamp = timestamp}); -} diff --git a/app/include/zmk/events/mouse_tick.h b/app/include/zmk/events/mouse_tick.h new file mode 100644 index 00000000..6307b5eb --- /dev/null +++ b/app/include/zmk/events/mouse_tick.h @@ -0,0 +1,36 @@ + +/* + * Copyright (c) 2020 The ZMK Contributors + * + * SPDX-License-Identifier: MIT + */ + +#pragma once + +#include +#include +#include +#include + +struct zmk_mouse_tick { + struct vector2d max_move; + struct vector2d max_scroll; + struct mouse_config move_config; + struct mouse_config scroll_config; + int64_t timestamp; +}; + +ZMK_EVENT_DECLARE(zmk_mouse_tick); + +static inline struct zmk_mouse_tick_event *zmk_mouse_tick(struct vector2d max_move, + struct vector2d max_scroll, + struct mouse_config move_config, + struct mouse_config scroll_config) { + return new_zmk_mouse_tick((struct zmk_mouse_tick){ + .max_move = max_move, + .max_scroll = max_scroll, + .move_config = move_config, + .scroll_config = scroll_config, + .timestamp = k_uptime_get(), + }); +} diff --git a/app/include/zmk/hid.h b/app/include/zmk/hid.h index b293aaa6..d6edac2c 100644 --- a/app/include/zmk/hid.h +++ b/app/include/zmk/hid.h @@ -242,7 +242,7 @@ static const uint8_t zmk_hid_report_desc[] = { /* REPORT_COUNT (2) */ HID_GI_REPORT_COUNT, 0x02, - /* USAGE (X) */ + /* USAGE (X) */ // Vertical scroll HID_LI_USAGE, HID_USAGE_GD_X, /* USAGE (Y) */ @@ -269,7 +269,7 @@ static const uint8_t zmk_hid_report_desc[] = { /* Input (Data,Var,Rel) */ HID_MI_INPUT, 0x06, - /* USAGE_PAGE (Consumer) */ // Horizontal wheel + /* USAGE_PAGE (Consumer) */ // Horizontal scroll HID_GI_USAGE_PAGE, HID_USAGE_CONSUMER, /* USAGE (AC Pan) */ @@ -333,8 +333,8 @@ struct zmk_hid_mouse_report_body { zmk_mouse_button_flags_t buttons; int16_t x; int16_t y; - int8_t wheel_vert; - int8_t wheel_hor; + int8_t scroll_y; + int8_t scroll_x; } __packed; struct zmk_hid_mouse_report { @@ -361,10 +361,10 @@ int zmk_hid_mouse_button_press(zmk_mouse_button_t button); int zmk_hid_mouse_button_release(zmk_mouse_button_t button); int zmk_hid_mouse_buttons_press(zmk_mouse_button_flags_t buttons); int zmk_hid_mouse_buttons_release(zmk_mouse_button_flags_t buttons); -int zmk_hid_mouse_movement_press(int16_t x, int16_t y); -int zmk_hid_mouse_movement_release(int16_t x, int16_t y); -int zmk_hid_mouse_wheel_press(int8_t hor, int8_t vert); -int zmk_hid_mouse_wheel_release(int8_t hor, int8_t vert); +void zmk_hid_mouse_movement_set(int16_t x, int16_t y); +void zmk_hid_mouse_scroll_set(int8_t x, int8_t y); +void zmk_hid_mouse_movement_update(int16_t x, int16_t y); +void zmk_hid_mouse_scroll_update(int8_t x, int8_t y); void zmk_hid_mouse_clear(); struct zmk_hid_keyboard_report *zmk_hid_get_keyboard_report(); diff --git a/app/include/zmk/mouse.h b/app/include/zmk/mouse.h index e749ac5d..93d96df9 100644 --- a/app/include/zmk/mouse.h +++ b/app/include/zmk/mouse.h @@ -11,3 +11,17 @@ typedef uint16_t zmk_mouse_button_flags_t; typedef uint16_t zmk_mouse_button_t; + +struct mouse_config { + int delay_ms; + int time_to_max_speed_ms; + // acceleration exponent 0: uniform speed + // acceleration exponent 1: uniform acceleration + // acceleration exponent 2: uniform jerk + float acceleration_exponent; +}; + +struct vector2d { + float x; + float y; +}; diff --git a/app/src/behaviors/behavior_mouse_key_press.c b/app/src/behaviors/behavior_mouse_key_press.c index 3dc5ce79..6da2a8c4 100644 --- a/app/src/behaviors/behavior_mouse_key_press.c +++ b/app/src/behaviors/behavior_mouse_key_press.c @@ -10,28 +10,29 @@ #include #include -#include -#include #include -#include -#include +#include +#include LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL); +#if DT_HAS_COMPAT_STATUS_OKAY(DT_DRV_COMPAT) + static int behavior_mouse_key_press_init(const struct device *dev) { return 0; }; static int on_keymap_binding_pressed(struct zmk_behavior_binding *binding, struct zmk_behavior_binding_event event) { LOG_DBG("position %d keycode 0x%02X", event.position, binding->param1); - zmk_hid_mouse_buttons_press(HID_USAGE_ID(binding->param1)); - return zmk_endpoints_send_mouse_report(); + + return ZMK_EVENT_RAISE( + zmk_mouse_button_state_changed_from_encoded(binding->param1, true, event.timestamp)); } static int on_keymap_binding_released(struct zmk_behavior_binding *binding, struct zmk_behavior_binding_event event) { LOG_DBG("position %d keycode 0x%02X", event.position, binding->param1); - zmk_hid_mouse_buttons_release(HID_USAGE_ID(binding->param1)); - return zmk_endpoints_send_mouse_report(); + return ZMK_EVENT_RAISE( + zmk_mouse_button_state_changed_from_encoded(binding->param1, false, event.timestamp)); } static const struct behavior_driver_api behavior_mouse_key_press_driver_api = { @@ -43,3 +44,5 @@ static const struct behavior_driver_api behavior_mouse_key_press_driver_api = { APPLICATION, CONFIG_KERNEL_INIT_PRIORITY_DEFAULT, &behavior_mouse_key_press_driver_api); DT_INST_FOREACH_STATUS_OKAY(KP_INST) + +#endif /* DT_HAS_COMPAT_STATUS_OKAY(DT_DRV_COMPAT) */ \ No newline at end of file diff --git a/app/src/behaviors/behavior_mouse_move.c b/app/src/behaviors/behavior_mouse_move.c index cc51129d..92ea3d86 100644 --- a/app/src/behaviors/behavior_mouse_move.c +++ b/app/src/behaviors/behavior_mouse_move.c @@ -10,37 +10,48 @@ #include #include -#include -#include #include -#include +#include +#include +#include LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL); +#if DT_HAS_COMPAT_STATUS_OKAY(DT_DRV_COMPAT) + static int behavior_mouse_move_init(const struct device *dev) { return 0; }; static int on_keymap_binding_pressed(struct zmk_behavior_binding *binding, struct zmk_behavior_binding_event event) { LOG_DBG("position %d keycode 0x%02X", event.position, binding->param1); - int res = ZMK_EVENT_RAISE( - zmk_mouse_state_changed_from_encoded(binding->param1, true, event.timestamp)); - return res; + const struct device *dev = device_get_binding(binding->behavior_dev); + const struct mouse_config *config = dev->config; + return ZMK_EVENT_RAISE( + zmk_mouse_move_state_changed_from_encoded(binding->param1, *config, true, event.timestamp)); } static int on_keymap_binding_released(struct zmk_behavior_binding *binding, struct zmk_behavior_binding_event event) { LOG_DBG("position %d keycode 0x%02X", event.position, binding->param1); - - return ZMK_EVENT_RAISE( - zmk_mouse_state_changed_from_encoded(binding->param1, false, event.timestamp)); + const struct device *dev = device_get_binding(binding->behavior_dev); + const struct mouse_config *config = dev->config; + return ZMK_EVENT_RAISE(zmk_mouse_move_state_changed_from_encoded(binding->param1, *config, + false, event.timestamp)); } static const struct behavior_driver_api behavior_mouse_move_driver_api = { .binding_pressed = on_keymap_binding_pressed, .binding_released = on_keymap_binding_released}; #define KP_INST(n) \ + static struct mouse_config behavior_mouse_move_config_##n = { \ + .delay_ms = DT_INST_PROP(n, delay_ms), \ + .time_to_max_speed_ms = DT_INST_PROP(n, time_to_max_speed_ms), \ + .acceleration_exponent = (float)DT_INST_PROP(n, acceleration_exponent) / 1000.0f, \ + }; \ DEVICE_AND_API_INIT(behavior_mouse_move_##n, DT_INST_LABEL(n), behavior_mouse_move_init, NULL, \ - NULL, APPLICATION, CONFIG_KERNEL_INIT_PRIORITY_DEFAULT, \ - &behavior_mouse_move_driver_api); + &behavior_mouse_move_config_##n, APPLICATION, \ + CONFIG_KERNEL_INIT_PRIORITY_DEFAULT, &behavior_mouse_move_driver_api); DT_INST_FOREACH_STATUS_OKAY(KP_INST) + +#endif /* DT_HAS_COMPAT_STATUS_OKAY(DT_DRV_COMPAT) */ diff --git a/app/src/behaviors/behavior_mouse_scroll.c b/app/src/behaviors/behavior_mouse_scroll.c new file mode 100644 index 00000000..e347d8af --- /dev/null +++ b/app/src/behaviors/behavior_mouse_scroll.c @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2021 The ZMK Contributors + * + * SPDX-License-Identifier: MIT + */ + +#define DT_DRV_COMPAT zmk_behavior_mouse_scroll + +#include +#include +#include + +#include +#include +#include +#include +#include + +LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL); + +#if DT_HAS_COMPAT_STATUS_OKAY(DT_DRV_COMPAT) + +static int behavior_mouse_scroll_init(const struct device *dev) { return 0; }; + +static int on_keymap_binding_pressed(struct zmk_behavior_binding *binding, + struct zmk_behavior_binding_event event) { + LOG_DBG("position %d keycode 0x%02X", event.position, binding->param1); + const struct device *dev = device_get_binding(binding->behavior_dev); + const struct mouse_config *config = dev->config; + return ZMK_EVENT_RAISE(zmk_mouse_scroll_state_changed_from_encoded(binding->param1, *config, + true, event.timestamp)); +} + +static int on_keymap_binding_released(struct zmk_behavior_binding *binding, + struct zmk_behavior_binding_event event) { + LOG_DBG("position %d keycode 0x%02X", event.position, binding->param1); + const struct device *dev = device_get_binding(binding->behavior_dev); + const struct mouse_config *config = dev->config; + return ZMK_EVENT_RAISE(zmk_mouse_scroll_state_changed_from_encoded(binding->param1, *config, + false, event.timestamp)); +} + +static const struct behavior_driver_api behavior_mouse_scroll_driver_api = { + .binding_pressed = on_keymap_binding_pressed, .binding_released = on_keymap_binding_released}; + +#define KP_INST(n) \ + static struct mouse_config behavior_mouse_scroll_config_##n = { \ + .delay_ms = DT_INST_PROP(n, delay_ms), \ + .time_to_max_speed_ms = DT_INST_PROP(n, time_to_max_speed_ms), \ + .acceleration_exponent = (float)DT_INST_PROP(n, acceleration_exponent) / 1000.0f, \ + }; \ + DEVICE_AND_API_INIT(behavior_mouse_scroll_##n, DT_INST_LABEL(n), behavior_mouse_scroll_init, \ + NULL, &behavior_mouse_scroll_config_##n, APPLICATION, \ + CONFIG_KERNEL_INIT_PRIORITY_DEFAULT, &behavior_mouse_scroll_driver_api); + +DT_INST_FOREACH_STATUS_OKAY(KP_INST) + +#endif /* DT_HAS_COMPAT_STATUS_OKAY(DT_DRV_COMPAT) */ diff --git a/app/src/behaviors/behavior_mouse_wheel.c b/app/src/behaviors/behavior_mouse_wheel.c deleted file mode 100644 index bd2cd29b..00000000 --- a/app/src/behaviors/behavior_mouse_wheel.c +++ /dev/null @@ -1,52 +0,0 @@ -/* - * Copyright (c) 2021 The ZMK Contributors - * - * SPDX-License-Identifier: MIT - */ - -#define DT_DRV_COMPAT zmk_behavior_mouse_wheel - -#include -#include -#include - -#include -#include -#include -#include -#include - -LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL); - -#define WHEEL_HORIZONTAL(encoded) (((encoded)&0xFF00) >> 8) -#define WHEEL_VERTICAL(encoded) ((encoded)&0x00FF) - -static int behavior_mouse_wheel_init(const struct device *dev) { return 0; }; - -static int on_keymap_binding_pressed(struct zmk_behavior_binding *binding, - struct zmk_behavior_binding_event event) { - LOG_DBG("position %d keycode 0x%02X", event.position, binding->param1); - int32_t x = WHEEL_HORIZONTAL(binding->param1); - int32_t y = WHEEL_VERTICAL(binding->param1); - zmk_hid_mouse_wheel_press(x, y); - return zmk_endpoints_send_mouse_report(); -} - -static int on_keymap_binding_released(struct zmk_behavior_binding *binding, - struct zmk_behavior_binding_event event) { - LOG_DBG("position %d keycode 0x%02X", event.position, binding->param1); - int32_t x = WHEEL_HORIZONTAL(binding->param1); - int32_t y = WHEEL_VERTICAL(binding->param1); - zmk_hid_mouse_wheel_release(x, y); - return zmk_endpoints_send_mouse_report(); -} - -static const struct behavior_driver_api behavior_mouse_wheel_driver_api = { - .binding_pressed = on_keymap_binding_pressed, .binding_released = on_keymap_binding_released}; - -#define KP_INST(n) \ - DEVICE_AND_API_INIT(behavior_mouse_wheel_##n, DT_INST_LABEL(n), behavior_mouse_wheel_init, \ - NULL, NULL, APPLICATION, CONFIG_KERNEL_INIT_PRIORITY_DEFAULT, \ - &behavior_mouse_wheel_driver_api); - -DT_INST_FOREACH_STATUS_OKAY(KP_INST) diff --git a/app/src/endpoints.c b/app/src/endpoints.c index 59d9f74e..d40396c8 100644 --- a/app/src/endpoints.c +++ b/app/src/endpoints.c @@ -146,7 +146,7 @@ int zmk_endpoints_send_report(uint16_t usage_page) { } int zmk_endpoints_send_mouse_report() { - LOG_ERR("SENDING MOUSE REPORT"); + LOG_DBG("SENDING MOUSE REPORT"); struct zmk_hid_mouse_report *mouse_report = zmk_hid_get_mouse_report(); switch (current_endpoint) { diff --git a/app/src/events/mouse_button_state_changed.c b/app/src/events/mouse_button_state_changed.c new file mode 100644 index 00000000..e1ede414 --- /dev/null +++ b/app/src/events/mouse_button_state_changed.c @@ -0,0 +1,10 @@ +/* + * Copyright (c) 2020 The ZMK Contributors + * + * SPDX-License-Identifier: MIT + */ + +#include +#include + +ZMK_EVENT_IMPL(zmk_mouse_button_state_changed); diff --git a/app/src/events/mouse_move_state_changed.c b/app/src/events/mouse_move_state_changed.c new file mode 100644 index 00000000..faf89cb8 --- /dev/null +++ b/app/src/events/mouse_move_state_changed.c @@ -0,0 +1,10 @@ +/* + * Copyright (c) 2020 The ZMK Contributors + * + * SPDX-License-Identifier: MIT + */ + +#include +#include + +ZMK_EVENT_IMPL(zmk_mouse_move_state_changed); diff --git a/app/src/events/mouse_scroll_state_changed.c b/app/src/events/mouse_scroll_state_changed.c new file mode 100644 index 00000000..4b4170fe --- /dev/null +++ b/app/src/events/mouse_scroll_state_changed.c @@ -0,0 +1,10 @@ +/* + * Copyright (c) 2020 The ZMK Contributors + * + * SPDX-License-Identifier: MIT + */ + +#include +#include + +ZMK_EVENT_IMPL(zmk_mouse_scroll_state_changed); diff --git a/app/src/events/mouse_state_changed.c b/app/src/events/mouse_tick.c similarity index 55% rename from app/src/events/mouse_state_changed.c rename to app/src/events/mouse_tick.c index 0d46ccf3..0930b9fb 100644 --- a/app/src/events/mouse_state_changed.c +++ b/app/src/events/mouse_tick.c @@ -5,6 +5,6 @@ */ #include -#include +#include -ZMK_EVENT_IMPL(zmk_mouse_state_changed); +ZMK_EVENT_IMPL(zmk_mouse_tick); diff --git a/app/src/hid.c b/app/src/hid.c index 85d87de1..9cdceac9 100644 --- a/app/src/hid.c +++ b/app/src/hid.c @@ -16,7 +16,7 @@ static struct zmk_hid_keyboard_report keyboard_report = { static struct zmk_hid_consumer_report consumer_report = {.report_id = 2, .body = {.keys = {0}}}; static struct zmk_hid_mouse_report mouse_report = { - .report_id = 4, .body = {.buttons = 0, .x = 0, .y = 0, .wheel_vert = 0, .wheel_hor = 0}}; + .report_id = 4, .body = {.buttons = 0, .x = 0, .y = 0, .scroll_x = 0, .scroll_y = 0}}; // Keep track of how often a modifier was pressed. // Only release the modifier if the count is 0. @@ -176,10 +176,6 @@ void zmk_hid_consumer_clear() { memset(&consumer_report.body, 0, sizeof(consumer // Only release the button if the count is 0. static int explicit_button_counts[16] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; static zmk_mod_flags_t explicit_buttons = 0; -static int16_t curr_x = 0; -static int16_t curr_y = 0; -static int8_t curr_hor = 0; -static int8_t curr_vert = 0; #define SET_MOUSE_BUTTONS(btns) \ { \ @@ -228,50 +224,31 @@ int zmk_hid_mouse_buttons_release(zmk_mouse_button_flags_t buttons) { return 0; } -#define SET_MOUSE_MOVEMENT(coor_x, coor_y) \ - { \ - mouse_report.body.x = coor_x; \ - LOG_DBG("Mouse movement x set to 0x%02X", mouse_report.body.x); \ - mouse_report.body.y = coor_y; \ - LOG_DBG("Mouse movement y set to 0x%02X", mouse_report.body.y); \ - } - -int zmk_hid_mouse_movement_press(int16_t x, int16_t y) { - curr_x += x; - curr_y += y; - SET_MOUSE_MOVEMENT(curr_x, curr_y); - return 0; +void zmk_hid_mouse_movement_set(int16_t x, int16_t y) { + mouse_report.body.x = x; + mouse_report.body.y = y; + LOG_DBG("Mouse movement set to 0x%02X 0x%02X ", mouse_report.body.x, mouse_report.body.y); } -int zmk_hid_mouse_movement_release(int16_t x, int16_t y) { - curr_x -= x; - curr_y -= y; - SET_MOUSE_MOVEMENT(curr_x, curr_y); - return 0; +void zmk_hid_mouse_movement_update(int16_t x, int16_t y) { + mouse_report.body.x += x; + mouse_report.body.y += y; + LOG_DBG("Mouse movement updated to 0x%02X 0x%02X ", mouse_report.body.x, mouse_report.body.y); } -#define SET_MOUSE_WHEEL(horiz, vertic) \ - { \ - mouse_report.body.wheel_hor = horiz; \ - LOG_DBG("Mouse wheel hor set to 0x%02X", mouse_report.body.wheel_hor); \ - mouse_report.body.wheel_vert = vertic; \ - LOG_DBG("Mouse wheel vert set to 0x%02X", mouse_report.body.wheel_vert); \ - } - -int zmk_hid_mouse_wheel_press(int8_t hor, int8_t vert) { - curr_hor += hor; - curr_vert += vert; - SET_MOUSE_WHEEL(curr_hor, curr_vert); - return 0; +void zmk_hid_mouse_scroll_set(int8_t x, int8_t y) { + mouse_report.body.scroll_x = x; + mouse_report.body.scroll_y = y; + LOG_DBG("Mouse scroll set to 0x%02X 0x%02X ", mouse_report.body.scroll_x, + mouse_report.body.scroll_y); } -int zmk_hid_mouse_wheel_release(int8_t hor, int8_t vert) { - curr_hor -= hor; - curr_vert -= vert; - SET_MOUSE_WHEEL(curr_hor, curr_vert); - return 0; +void zmk_hid_mouse_scroll_update(int8_t x, int8_t y) { + mouse_report.body.scroll_x += x; + mouse_report.body.scroll_y += y; + LOG_DBG("Mouse scroll updated to 0x%02X 0x%02X ", mouse_report.body.scroll_x, + mouse_report.body.scroll_y); } - void zmk_hid_mouse_clear() { memset(&mouse_report.body, 0, sizeof(mouse_report.body)); } struct zmk_hid_keyboard_report *zmk_hid_get_keyboard_report() { diff --git a/app/src/hid_listener.c b/app/src/hid_listener.c index a6877244..af7c38d8 100644 --- a/app/src/hid_listener.c +++ b/app/src/hid_listener.c @@ -11,8 +11,6 @@ LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL); #include #include -#include -#include #include #include #include @@ -71,62 +69,6 @@ static int hid_listener_keycode_released(const struct zmk_keycode_state_changed return zmk_endpoints_send_report(ev->usage_page); } -static void zmk_mouse_work(struct k_work *work) { - int rc = zmk_endpoints_send_mouse_report(); - if (rc != 0) { - LOG_ERR("Failed to send mouse report, error: %d", rc); - } -} - -K_WORK_DEFINE(mouse_work, &zmk_mouse_work); - -void mouse_timer_cb(struct k_timer *dummy) { k_work_submit(&mouse_work); } - -K_TIMER_DEFINE(mouse_timer, mouse_timer_cb, mouse_timer_cb); - -static int mouse_timer_ref_count = 0; - -void mouse_timer_ref() { - if (mouse_timer_ref_count == 0) { - k_timer_start(&mouse_timer, K_NO_WAIT, K_MSEC(10)); - } - mouse_timer_ref_count += 1; -} - -void mouse_timer_unref() { - if (mouse_timer_ref_count > 0) { - mouse_timer_ref_count -= 1; - } - if (mouse_timer_ref_count == 0) { - k_timer_stop(&mouse_timer); - } -} - -static int hid_listener_mouse_pressed(const struct zmk_mouse_state_changed *ev) { - int err; - LOG_DBG("x: 0x%02X, y: 0x%02X", ev->x, ev->y); - err = zmk_hid_mouse_movement_press(ev->x, ev->y); - if (err) { - LOG_ERR("Unable to press button"); - return err; - } - mouse_timer_ref(); - return 0; -} - -static int hid_listener_mouse_released(const struct zmk_mouse_state_changed *ev) { - int err; - LOG_DBG("x: 0x%02X, y: 0x%02X", ev->x, ev->y); - err = zmk_hid_mouse_movement_release(ev->x, ev->y); - if (err) { - LOG_ERR("Unable to release button"); - mouse_timer_unref(); - return err; - } - mouse_timer_unref(); - return 0; -} - int hid_listener(const zmk_event_t *eh) { const struct zmk_keycode_state_changed *kc_ev = as_zmk_keycode_state_changed(eh); if (kc_ev) { @@ -137,18 +79,8 @@ int hid_listener(const zmk_event_t *eh) { } return 0; } - const struct zmk_mouse_state_changed *ms_ev = as_zmk_mouse_state_changed(eh); - if (ms_ev) { - if (ms_ev->state) { - hid_listener_mouse_pressed(ms_ev); - } else { - hid_listener_mouse_released(ms_ev); - } - return 0; - } return 0; } ZMK_LISTENER(hid_listener, hid_listener); -ZMK_SUBSCRIPTION(hid_listener, zmk_keycode_state_changed); -ZMK_SUBSCRIPTION(hid_listener, zmk_mouse_state_changed); +ZMK_SUBSCRIPTION(hid_listener, zmk_keycode_state_changed); \ No newline at end of file diff --git a/app/src/mouse/key_listener.c b/app/src/mouse/key_listener.c new file mode 100644 index 00000000..6a96fbaa --- /dev/null +++ b/app/src/mouse/key_listener.c @@ -0,0 +1,135 @@ +/* + * Copyright (c) 2020 The ZMK Contributors + * + * SPDX-License-Identifier: MIT + */ + +#include +#include + +LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL); + +#include +#include +#include +#include +#include +#include +#include +#include + +static struct vector2d move_speed = {0}; +static struct vector2d scroll_speed = {0}; +static struct mouse_config move_config = {0}; +static struct mouse_config scroll_config = {0}; + +static void clear_mouse_state() { + move_speed = (struct vector2d){0}; + scroll_speed = (struct vector2d){0}; +} + +static void mouse_tick_timer_handler(struct k_work *work) { + zmk_hid_mouse_movement_set(0, 0); + zmk_hid_mouse_scroll_set(0, 0); + ZMK_EVENT_RAISE(zmk_mouse_tick(move_speed, scroll_speed, move_config, scroll_config)); + zmk_endpoints_send_mouse_report(); +} + +K_WORK_DEFINE(mouse_tick, &mouse_tick_timer_handler); + +void mouse_timer_cb(struct k_timer *dummy) { k_work_submit(&mouse_tick); } + +K_TIMER_DEFINE(mouse_timer, mouse_timer_cb, mouse_timer_cb); + +static int mouse_timer_ref_count = 0; + +void mouse_timer_ref() { + if (mouse_timer_ref_count == 0) { + k_timer_start(&mouse_timer, K_NO_WAIT, K_MSEC(10)); + } + mouse_timer_ref_count += 1; + // trigger the first mouse tick event immediately + mouse_tick_timer_handler(NULL); +} + +void mouse_timer_unref() { + if (mouse_timer_ref_count > 0) { + mouse_timer_ref_count--; + } + if (mouse_timer_ref_count == 0) { + k_timer_stop(&mouse_timer); + clear_mouse_state(); + } +} + +static void listener_mouse_move_pressed(const struct zmk_mouse_move_state_changed *ev) { + move_speed.x += ev->max_speed.x; + move_speed.y += ev->max_speed.y; + mouse_timer_ref(); +} + +static void listener_mouse_move_released(const struct zmk_mouse_move_state_changed *ev) { + move_speed.x -= ev->max_speed.x; + move_speed.y -= ev->max_speed.y; + mouse_timer_unref(); +} + +static void listener_mouse_scroll_pressed(const struct zmk_mouse_scroll_state_changed *ev) { + scroll_speed.x += ev->max_speed.x; + scroll_speed.y += ev->max_speed.y; + mouse_timer_ref(); +} + +static void listener_mouse_scroll_released(const struct zmk_mouse_scroll_state_changed *ev) { + scroll_speed.x -= ev->max_speed.x; + scroll_speed.y -= ev->max_speed.y; + mouse_timer_unref(); +} + +static void listener_mouse_button_pressed(const struct zmk_mouse_button_state_changed *ev) { + LOG_DBG("buttons: 0x%02X", ev->buttons); + zmk_hid_mouse_buttons_press(ev->buttons); + zmk_endpoints_send_mouse_report(); +} + +static void listener_mouse_button_released(const struct zmk_mouse_button_state_changed *ev) { + LOG_DBG("buttons: 0x%02X", ev->buttons); + zmk_hid_mouse_buttons_release(ev->buttons); + zmk_endpoints_send_mouse_report(); +} + +int mouse_listener(const zmk_event_t *eh) { + const struct zmk_mouse_move_state_changed *mmv_ev = as_zmk_mouse_move_state_changed(eh); + if (mmv_ev) { + if (mmv_ev->state) { + listener_mouse_move_pressed(mmv_ev); + } else { + listener_mouse_move_released(mmv_ev); + } + return 0; + } + const struct zmk_mouse_scroll_state_changed *msc_ev = as_zmk_mouse_scroll_state_changed(eh); + if (msc_ev) { + if (msc_ev->state) { + listener_mouse_scroll_pressed(msc_ev); + } else { + listener_mouse_scroll_released(msc_ev); + } + return 0; + } + const struct zmk_mouse_button_state_changed *mbt_ev = as_zmk_mouse_button_state_changed(eh); + if (mbt_ev) { + if (mbt_ev->state) { + listener_mouse_button_pressed(mbt_ev); + } else { + listener_mouse_button_released(mbt_ev); + } + return 0; + } + return 0; +} + +ZMK_LISTENER(mouse_listener, mouse_listener); +ZMK_SUBSCRIPTION(mouse_listener, zmk_mouse_button_state_changed); +ZMK_SUBSCRIPTION(mouse_listener, zmk_mouse_move_state_changed); +ZMK_SUBSCRIPTION(mouse_listener, zmk_mouse_scroll_state_changed); diff --git a/app/src/mouse/tick_listener.c b/app/src/mouse/tick_listener.c new file mode 100644 index 00000000..a80b6069 --- /dev/null +++ b/app/src/mouse/tick_listener.c @@ -0,0 +1,129 @@ +/* + * Copyright (c) 2020 The ZMK Contributors + * + * SPDX-License-Identifier: MIT + */ + +#include + +LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL); + +#include +#include +#include +#include + +#include + +// CLAMP will be provided by sys/util.h from zephyr 2.6 onward +#define CLAMP(x, min, max) MIN(MAX(x, min), max) + +#if CONFIG_MINIMAL_LIBC +static float powf(float base, float exponent) { + // poor man's power implementation rounds the exponent down to the nearest integer. + LOG_DBG("falling back to integer exponent %d instead of %f", (int)exponent, exponent); + float power = 1.0f; + for (; exponent < 1.0f; exponent--) { + power = power * base; + } + return power; +} +#else +#include +#endif + +struct movement_state { + int64_t start; + int64_t last_tick; + struct vector2d fractional_remainder; +}; + +struct movement_state move_state = {0}; +static struct mouse_config move_config = (struct mouse_config){ + .delay_ms = 0, + .time_to_max_speed_ms = 300, + .acceleration_exponent = 2.0, +}; + +struct movement_state scroll_state = {0}; +static struct mouse_config scroll_config = (struct mouse_config){ + .delay_ms = 0, + .time_to_max_speed_ms = 300, + .acceleration_exponent = 2.0, +}; + +static int64_t ms_since_last_tick(int64_t last_tick, int64_t now) { return now - last_tick; } + +static int64_t ms_since_start(int64_t start, int64_t now) { + int64_t move_duration = now - start; + // start can be in the future if there's a delay + if (move_duration < 0) { + move_duration = 0; + } + return move_duration; +} + +static float speed(struct mouse_config *config, float max_speed, int64_t duration_ms) { + // Calculate the speed based on MouseKeysAccel + // See https://en.wikipedia.org/wiki/Mouse_keys + if (duration_ms > config->time_to_max_speed_ms) { + return max_speed; + } + float time_fraction = (float)duration_ms / config->time_to_max_speed_ms; + return max_speed * powf(time_fraction, config->acceleration_exponent); +} + +static void track_remainder(float *move, float *remainder) { + float new_move = *move + *remainder; + *remainder = new_move - (int)new_move; + *move = (int)new_move; +} + +static struct vector2d update_movement(struct movement_state *state, struct mouse_config *config, + struct vector2d max_speed, int64_t now) { + struct vector2d move = {0}; + if (max_speed.x == 0 && max_speed.y == 0) { + *state = (struct movement_state){0}; + return move; + } + if (state->start == 0) { + state->start = now + config->delay_ms; + state->last_tick = now; + } + + int64_t tick_duration = ms_since_last_tick(state->last_tick, now); + int64_t move_duration = ms_since_start(state->start, now); + move = (struct vector2d){ + .x = speed(config, max_speed.x, move_duration) * tick_duration / 1000, + .y = speed(config, max_speed.y, move_duration) * tick_duration / 1000, + }; + + track_remainder(&(move.x), &state->fractional_remainder.x); + track_remainder(&(move.y), &state->fractional_remainder.y); + + state->last_tick = now; + return move; +} + +static void mouse_tick_handler(const struct zmk_mouse_tick *tick) { + struct vector2d move = + update_movement(&move_state, &move_config, tick->max_move, tick->timestamp); + zmk_hid_mouse_movement_update((int16_t)CLAMP(move.x, INT16_MIN, INT16_MAX), + (int16_t)CLAMP(move.y, INT16_MIN, INT16_MAX)); + struct vector2d scroll = + update_movement(&scroll_state, &scroll_config, tick->max_scroll, tick->timestamp); + zmk_hid_mouse_scroll_update((int8_t)CLAMP(scroll.x, INT8_MIN, INT8_MAX), + (int8_t)CLAMP(scroll.y, INT8_MIN, INT8_MAX)); +} + +int zmk_mouse_tick_listener(const zmk_event_t *eh) { + const struct zmk_mouse_tick *tick = as_zmk_mouse_tick(eh); + if (tick) { + mouse_tick_handler(tick); + return 0; + } + return 0; +} + +ZMK_LISTENER(zmk_mouse_tick_listener, zmk_mouse_tick_listener); +ZMK_SUBSCRIPTION(zmk_mouse_tick_listener, zmk_mouse_tick); \ No newline at end of file diff --git a/app/tests/mouse-keys/mmv/events.patterns b/app/tests/mouse-keys/mmv/events.patterns new file mode 100644 index 00000000..833100f6 --- /dev/null +++ b/app/tests/mouse-keys/mmv/events.patterns @@ -0,0 +1 @@ +s/.*hid_listener_keycode_//p \ No newline at end of file diff --git a/app/tests/mouse-keys/mmv/keycode_events.snapshot b/app/tests/mouse-keys/mmv/keycode_events.snapshot new file mode 100644 index 00000000..259501ba --- /dev/null +++ b/app/tests/mouse-keys/mmv/keycode_events.snapshot @@ -0,0 +1,2 @@ +pressed: usage_page 0x07 keycode 0x05 implicit_mods 0x00 explicit_mods 0x00 +released: usage_page 0x07 keycode 0x05 implicit_mods 0x00 explicit_mods 0x00 diff --git a/app/tests/mouse-keys/mmv/native_posix.keymap b/app/tests/mouse-keys/mmv/native_posix.keymap new file mode 100644 index 00000000..ecf06601 --- /dev/null +++ b/app/tests/mouse-keys/mmv/native_posix.keymap @@ -0,0 +1,26 @@ +#include +#include +#include +#include + +/ { + keymap { + compatible = "zmk,keymap"; + label ="Default keymap"; + + default_layer { + bindings = < + &mmv MOVE_LEFT &none + &none &none + >; + }; + }; +}; + + +&kscan { + events = < + ZMK_MOCK_PRESS(0,0,100) + ZMK_MOCK_RELEASE(0,0,10) + >; +}; \ No newline at end of file diff --git a/docs/docs/behaviors/mouse-emulation.md b/docs/docs/behaviors/mouse-emulation.md index e3b74048..fec7e2c6 100644 --- a/docs/docs/behaviors/mouse-emulation.md +++ b/docs/docs/behaviors/mouse-emulation.md @@ -5,7 +5,7 @@ sidebar_label: Mouse Emulation ## Summary -Mouse emulation behaviors send mouse movements, button presses or wheel actions. +Mouse emulation behaviors send mouse movements, button presses or scroll actions. Please view [`dt-bindings/zmk/mouse.h`](https://github.com/zmkfirmware/zmk/blob/main/app/include/dt-bindings/zmk/mouse.h) for a comprehensive list of signals. @@ -19,7 +19,7 @@ provided by ZMK near the top: #include ``` -Doing so allows using a set of defines such as `MOVE_UP`, `MOVE_DOWN`, `LCLK` and `WHEEL_UP` with these behaviors. +Doing so allows using a set of defines such as `MOVE_UP`, `MOVE_DOWN`, `LCLK` and `SCROLL_UP` with these behaviors. ## Mouse Button Press @@ -65,5 +65,5 @@ This behaviour is used to scroll, both horizontally and vertically. Example: ``` -&mwh WHEEL_UP +&mwh SCROLL_UP ```