zmk-config/patches/mouse-keys.patch

3337 lines
110 KiB
Diff

From b1deea5cc281d226e603f4b44462b9c661432816 Mon Sep 17 00:00:00 2001
From: Alexander Krikun <krikun98@gmail.com>
Date: Tue, 27 Apr 2021 18:24:11 +0300
Subject: [PATCH 1/2] The original changes from #778
A squashed version of the original changeset from
https://github.com/zmkfirmware/zmk/pull/778 rebased on main
---
app/CMakeLists.txt | 10 ++
app/Kconfig | 11 ++
app/dts/behaviors.dtsi | 5 +-
app/dts/behaviors/mouse_key_press.dtsi | 9 +
app/dts/behaviors/mouse_move.dtsi | 12 ++
app/dts/behaviors/mouse_scroll.dtsi | 12 ++
.../zmk,behavior-mouse-key-press.yaml | 5 +
.../behaviors/zmk,behavior-mouse-move.yaml | 13 ++
.../behaviors/zmk,behavior-mouse-scroll.yaml | 13 ++
app/include/dt-bindings/zmk/hid_usage_pages.h | 1 +
app/include/dt-bindings/zmk/mouse.h | 55 ++++++
app/include/zmk/endpoints.h | 1 +
.../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_tick.h | 39 +++++
app/include/zmk/hid.h | 137 ++++++++++++++-
app/include/zmk/hog.h | 1 +
app/include/zmk/mouse.h | 30 ++++
app/src/behaviors/behavior_mouse_key_press.c | 48 ++++++
app/src/behaviors/behavior_mouse_move.c | 57 +++++++
app/src/behaviors/behavior_mouse_scroll.c | 58 +++++++
app/src/endpoints.c | 35 ++++
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 ++
app/src/events/mouse_tick.c | 10 ++
app/src/hid.c | 86 ++++++++++
app/src/hid_listener.c | 12 +-
app/src/hog.c | 89 ++++++++++
app/src/main.c | 8 +
app/src/mouse/Kconfig | 38 +++++
app/src/mouse/key_listener.c | 160 ++++++++++++++++++
app/src/mouse/main.c | 30 ++++
app/src/mouse/tick_listener.c | 102 +++++++++++
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 | 110 ++++++++++++
docs/sidebars.js | 1 +
40 files changed, 1342 insertions(+), 9 deletions(-)
create mode 100644 app/dts/behaviors/mouse_key_press.dtsi
create mode 100644 app/dts/behaviors/mouse_move.dtsi
create mode 100644 app/dts/behaviors/mouse_scroll.dtsi
create mode 100644 app/dts/bindings/behaviors/zmk,behavior-mouse-key-press.yaml
create mode 100644 app/dts/bindings/behaviors/zmk,behavior-mouse-move.yaml
create mode 100644 app/dts/bindings/behaviors/zmk,behavior-mouse-scroll.yaml
create mode 100644 app/include/dt-bindings/zmk/mouse.h
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
create mode 100644 app/include/zmk/events/mouse_tick.h
create mode 100644 app/include/zmk/mouse.h
create mode 100644 app/src/behaviors/behavior_mouse_key_press.c
create mode 100644 app/src/behaviors/behavior_mouse_move.c
create mode 100644 app/src/behaviors/behavior_mouse_scroll.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
create mode 100644 app/src/events/mouse_tick.c
create mode 100644 app/src/mouse/Kconfig
create mode 100644 app/src/mouse/key_listener.c
create mode 100644 app/src/mouse/main.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
create mode 100644 docs/docs/behaviors/mouse-emulation.md
diff --git a/app/CMakeLists.txt b/app/CMakeLists.txt
index 4b61fc7217..351505ad77 100644
--- a/app/CMakeLists.txt
+++ b/app/CMakeLists.txt
@@ -24,6 +24,9 @@ target_sources(app PRIVATE src/stdlib.c)
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/mouse/key_listener.c)
+target_sources(app PRIVATE src/mouse/main.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)
@@ -31,6 +34,10 @@ target_sources_ifdef(CONFIG_ZMK_EXT_POWER app PRIVATE src/ext_power_generic.c)
target_sources(app PRIVATE src/events/activity_state_changed.c)
target_sources(app PRIVATE src/events/position_state_changed.c)
target_sources(app PRIVATE src/events/sensor_event.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_USB_DEVICE_STACK app PRIVATE src/events/usb_conn_state_changed.c)
target_sources(app PRIVATE src/behaviors/behavior_reset.c)
@@ -53,6 +60,9 @@ if ((NOT CONFIG_ZMK_SPLIT) OR CONFIG_ZMK_SPLIT_ROLE_CENTRAL)
target_sources(app PRIVATE src/behaviors/behavior_transparent.c)
target_sources(app PRIVATE src/behaviors/behavior_none.c)
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_scroll.c)
target_sources(app PRIVATE src/combo.c)
target_sources(app PRIVATE src/behavior_queue.c)
target_sources(app PRIVATE src/conditional_layer.c)
diff --git a/app/Kconfig b/app/Kconfig
index f89d3279d1..3c59605814 100644
--- a/app/Kconfig
+++ b/app/Kconfig
@@ -127,6 +127,10 @@ config ZMK_BLE_CONSUMER_REPORT_QUEUE_SIZE
int "Max number of consumer HID reports to queue for sending over BLE"
default 5
+config ZMK_BLE_MOUSE_REPORT_QUEUE_SIZE
+ int "Max number of mouse HID reports to queue for sending over BLE"
+ default 20
+
config ZMK_BLE_CLEAR_BONDS_ON_START
bool "Configuration that clears all bond information from the keyboard on startup."
default n
@@ -285,6 +289,13 @@ endif
#Display/LED Options
endmenu
+menu "Mouse Options"
+
+rsource "src/mouse/Kconfig"
+
+#Mouse Options
+endmenu
+
menu "Power Management"
config ZMK_IDLE_TIMEOUT
diff --git a/app/dts/behaviors.dtsi b/app/dts/behaviors.dtsi
index b3502cbbc1..77eccf912f 100644
--- a/app/dts/behaviors.dtsi
+++ b/app/dts/behaviors.dtsi
@@ -18,4 +18,7 @@
#include <behaviors/caps_word.dtsi>
#include <behaviors/key_repeat.dtsi>
#include <behaviors/backlight.dtsi>
-#include <behaviors/macros.dtsi>
\ No newline at end of file
+#include <behaviors/macros.dtsi>
+#include <behaviors/mouse_key_press.dtsi>
+#include <behaviors/mouse_move.dtsi>
+#include <behaviors/mouse_scroll.dtsi>
diff --git a/app/dts/behaviors/mouse_key_press.dtsi b/app/dts/behaviors/mouse_key_press.dtsi
new file mode 100644
index 0000000000..8b2aacb366
--- /dev/null
+++ b/app/dts/behaviors/mouse_key_press.dtsi
@@ -0,0 +1,9 @@
+/ {
+ behaviors {
+ /omit-if-no-ref/ mkp: behavior_mouse_key_press {
+ compatible = "zmk,behavior-mouse-key-press";
+ label = "MOUSE_KEY_PRESS";
+ #binding-cells = <1>;
+ };
+ };
+};
diff --git a/app/dts/behaviors/mouse_move.dtsi b/app/dts/behaviors/mouse_move.dtsi
new file mode 100644
index 0000000000..d34329c806
--- /dev/null
+++ b/app/dts/behaviors/mouse_move.dtsi
@@ -0,0 +1,12 @@
+/ {
+ behaviors {
+ /omit-if-no-ref/ mmv: behavior_mouse_move {
+ compatible = "zmk,behavior-mouse-move";
+ label = "MOUSE_MOVE";
+ #binding-cells = <1>;
+ delay-ms = <0>;
+ time-to-max-speed-ms = <300>;
+ acceleration-exponent = <1>;
+ };
+ };
+};
diff --git a/app/dts/behaviors/mouse_scroll.dtsi b/app/dts/behaviors/mouse_scroll.dtsi
new file mode 100644
index 0000000000..fb54886dcb
--- /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 = <300>;
+ acceleration-exponent = <0>;
+ };
+ };
+};
diff --git a/app/dts/bindings/behaviors/zmk,behavior-mouse-key-press.yaml b/app/dts/bindings/behaviors/zmk,behavior-mouse-key-press.yaml
new file mode 100644
index 0000000000..8540916b72
--- /dev/null
+++ b/app/dts/bindings/behaviors/zmk,behavior-mouse-key-press.yaml
@@ -0,0 +1,5 @@
+description: Mouse key press/release behavior
+
+compatible: "zmk,behavior-mouse-key-press"
+
+include: one_param.yaml
diff --git a/app/dts/bindings/behaviors/zmk,behavior-mouse-move.yaml b/app/dts/bindings/behaviors/zmk,behavior-mouse-move.yaml
new file mode 100644
index 0000000000..73ec34ec2d
--- /dev/null
+++ b/app/dts/bindings/behaviors/zmk,behavior-mouse-move.yaml
@@ -0,0 +1,13 @@
+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 0000000000..5a932bc590
--- /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/include/dt-bindings/zmk/hid_usage_pages.h b/app/include/dt-bindings/zmk/hid_usage_pages.h
index 2ccdba5540..7fa54fd88b 100644
--- a/app/include/dt-bindings/zmk/hid_usage_pages.h
+++ b/app/include/dt-bindings/zmk/hid_usage_pages.h
@@ -26,6 +26,7 @@
#define HID_USAGE_GDV (0x06) // Generic Device Controls
#define HID_USAGE_KEY (0x07) // Keyboard/Keypad
#define HID_USAGE_LED (0x08) // LED
+#define HID_USAGE_BUTTON (0x09) // Button
#define HID_USAGE_TELEPHONY (0x0B) // Telephony Device
#define HID_USAGE_CONSUMER (0x0C) // Consumer
#define HID_USAGE_DIGITIZERS (0x0D) // Digitizers
diff --git a/app/include/dt-bindings/zmk/mouse.h b/app/include/dt-bindings/zmk/mouse.h
new file mode 100644
index 0000000000..cf0415c9b2
--- /dev/null
+++ b/app/include/dt-bindings/zmk/mouse.h
@@ -0,0 +1,55 @@
+/*
+ * Copyright (c) 2020 The ZMK Contributors
+ *
+ * SPDX-License-Identifier: MIT
+ */
+#pragma once
+
+/* Mouse press behavior */
+/* Left click */
+#define MB1 (0x01)
+#define LCLK (MB1)
+
+/* Right click */
+#define MB2 (0x02)
+#define RCLK (MB2)
+
+/* Middle click */
+#define MB3 (0x04)
+#define MCLK (MB3)
+
+#define MB4 (0x08)
+
+#define MB5 (0x10)
+
+#define MB6 (0x20)
+
+#define MB7 (0x40)
+
+#define MB8 (0x80)
+
+/* Mouse move behavior */
+#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))
+
+#define MOVE_UP MOVE_VERT(-600)
+#define MOVE_DOWN MOVE_VERT(600)
+#define MOVE_LEFT MOVE_HOR(-600)
+#define MOVE_RIGHT MOVE_HOR(600)
+
+/* 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 SCROLL(hor, vert) (SCROLL_HOR(hor) + SCROLL_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/endpoints.h b/app/include/zmk/endpoints.h
index c8860533e1..450d7ea370 100644
--- a/app/include/zmk/endpoints.h
+++ b/app/include/zmk/endpoints.h
@@ -13,3 +13,4 @@ int zmk_endpoints_toggle();
enum zmk_endpoint zmk_endpoints_selected();
int zmk_endpoints_send_report(uint16_t usage_page);
+int zmk_endpoints_send_mouse_report();
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 0000000000..7ec4d2087c
--- /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 <zephyr.h>
+#include <zmk/hid.h>
+#include <zmk/event_manager.h>
+#include <zmk/mouse.h>
+
+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 0000000000..8866f81d4e
--- /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 <zephyr.h>
+#include <zmk/event_manager.h>
+#include <zmk/mouse.h>
+
+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 0000000000..fa60e8a742
--- /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 <zephyr.h>
+#include <zmk/event_manager.h>
+#include <zmk/mouse.h>
+#include <dt-bindings/zmk/mouse.h>
+
+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_tick.h b/app/include/zmk/events/mouse_tick.h
new file mode 100644
index 0000000000..c75b9b4f86
--- /dev/null
+++ b/app/include/zmk/events/mouse_tick.h
@@ -0,0 +1,39 @@
+
+/*
+ * Copyright (c) 2020 The ZMK Contributors
+ *
+ * SPDX-License-Identifier: MIT
+ */
+
+#pragma once
+
+#include <dt-bindings/zmk/mouse.h>
+#include <zephyr.h>
+#include <zmk/event_manager.h>
+#include <zmk/mouse.h>
+
+struct zmk_mouse_tick {
+ struct vector2d max_move;
+ struct vector2d max_scroll;
+ struct mouse_config move_config;
+ struct mouse_config scroll_config;
+ int64_t *start_time;
+ 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,
+ int64_t *movement_start) {
+ 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,
+ .start_time = movement_start,
+ .timestamp = k_uptime_get(),
+ });
+}
diff --git a/app/include/zmk/hid.h b/app/include/zmk/hid.h
index 902b76d15a..aa26cd3a54 100644
--- a/app/include/zmk/hid.h
+++ b/app/include/zmk/hid.h
@@ -10,11 +10,10 @@
#include <usb/class/usb_hid.h>
#include <zmk/keys.h>
+#include <zmk/mouse.h>
#include <dt-bindings/zmk/hid_usage.h>
#include <dt-bindings/zmk/hid_usage_pages.h>
-#define ZMK_HID_KEYBOARD_NKRO_MAX_USAGE HID_USAGE_KEY_KEYPAD_EQUAL
-
#define COLLECTION_REPORT 0x03
static const uint8_t zmk_hid_report_desc[] = {
@@ -89,6 +88,116 @@ static const uint8_t zmk_hid_report_desc[] = {
/* INPUT (Data,Ary,Abs) */
HID_INPUT(0x00),
HID_END_COLLECTION,
+
+ /* USAGE_PAGE (Generic Desktop) */
+ HID_GI_USAGE_PAGE,
+ HID_USAGE_GD,
+ /* USAGE (Mouse) */
+ HID_LI_USAGE,
+ HID_USAGE_GD_MOUSE,
+ /* COLLECTION (Application) */
+ HID_MI_COLLECTION,
+ COLLECTION_APPLICATION,
+ /* REPORT ID (4) */
+ HID_GI_REPORT_ID,
+ 0x04,
+ /* USAGE (Pointer) */
+ HID_LI_USAGE,
+ HID_USAGE_GD_POINTER,
+ /* COLLECTION (Physical) */
+ HID_MI_COLLECTION,
+ COLLECTION_PHYSICAL,
+ /* USAGE_PAGE (Button) */
+ HID_GI_USAGE_PAGE,
+ HID_USAGE_BUTTON,
+ /* USAGE_MINIMUM (0x1) (button 1?) */
+ HID_LI_USAGE_MIN(1),
+ 0x1,
+ /* USAGE_MAXIMUM (0x10) (button 5? Buttons up to 8 still work) */
+ HID_LI_USAGE_MAX(1),
+ 0x10,
+ /* LOGICAL_MINIMUM (0) */
+ HID_GI_LOGICAL_MIN(1),
+ 0x00,
+ /* LOGICAL_MAXIMUM (1) */
+ HID_GI_LOGICAL_MAX(1),
+ 0x01,
+ /* REPORT_SIZE (1) */
+ HID_GI_REPORT_SIZE,
+ 0x01,
+ /* REPORT_COUNT (16) */
+ HID_GI_REPORT_COUNT,
+ 0x10,
+ /* INPUT (Data,Var,Abs) */
+ HID_MI_INPUT,
+ 0x02,
+ /* USAGE_PAGE (Generic Desktop) */
+ HID_GI_USAGE_PAGE,
+ HID_USAGE_GD,
+ /* LOGICAL_MINIMUM (-32767) */
+ HID_GI_LOGICAL_MIN(2),
+ 0x01,
+ 0x80,
+ /* LOGICAL_MAXIMUM (32767) */
+ HID_GI_LOGICAL_MAX(2),
+ 0xFF,
+ 0x7F,
+ /* REPORT_SIZE (16) */
+ HID_GI_REPORT_SIZE,
+ 0x10,
+ /* REPORT_COUNT (2) */
+ HID_GI_REPORT_COUNT,
+ 0x02,
+ /* USAGE (X) */ // Vertical scroll
+ HID_LI_USAGE,
+ HID_USAGE_GD_X,
+ /* USAGE (Y) */
+ HID_LI_USAGE,
+ HID_USAGE_GD_Y,
+ /* Input (Data,Var,Rel) */
+ HID_MI_INPUT,
+ 0x06,
+ /* LOGICAL_MINIMUM (-127) */
+ HID_GI_LOGICAL_MIN(1),
+ 0x81,
+ /* LOGICAL_MAXIMUM (127) */
+ HID_GI_LOGICAL_MAX(1),
+ 0x7F,
+ /* REPORT_SIZE (8) */
+ HID_GI_REPORT_SIZE,
+ 0x08,
+ /* REPORT_COUNT (1) */
+ HID_GI_REPORT_COUNT,
+ 0x01,
+ /* USAGE (Wheel) */
+ HID_LI_USAGE,
+ HID_USAGE_GD_WHEEL,
+ /* Input (Data,Var,Rel) */
+ HID_MI_INPUT,
+ 0x06,
+ /* USAGE_PAGE (Consumer) */ // Horizontal scroll
+ HID_GI_USAGE_PAGE,
+ HID_USAGE_CONSUMER,
+ /* USAGE (AC Pan) */
+ 0x0A,
+ 0x38,
+ 0x02,
+ /* LOGICAL_MINIMUM (-127) */
+ HID_GI_LOGICAL_MIN(1),
+ 0x81,
+ /* LOGICAL_MAXIMUM (127) */
+ HID_GI_LOGICAL_MAX(1),
+ 0x7F,
+ /* REPORT_COUNT (1) */
+ HID_GI_REPORT_COUNT,
+ 0x01,
+ /* Input (Data,Var,Rel) */
+ HID_MI_INPUT,
+ 0x06,
+ /* END COLLECTION */
+ HID_MI_COLLECTION_END,
+ /* END COLLECTION */
+ HID_MI_COLLECTION_END,
};
// struct zmk_hid_boot_report
@@ -126,6 +235,19 @@ struct zmk_hid_consumer_report {
struct zmk_hid_consumer_report_body body;
} __packed;
+struct zmk_hid_mouse_report_body {
+ zmk_mouse_button_flags_t buttons;
+ int16_t x;
+ int16_t y;
+ int8_t scroll_y;
+ int8_t scroll_x;
+} __packed;
+
+struct zmk_hid_mouse_report {
+ uint8_t report_id;
+ struct zmk_hid_mouse_report_body body;
+} __packed;
+
zmk_mod_flags_t zmk_hid_get_explicit_mods();
int zmk_hid_register_mod(zmk_mod_t modifier);
int zmk_hid_unregister_mod(zmk_mod_t modifier);
@@ -150,5 +272,16 @@ int zmk_hid_press(uint32_t usage);
int zmk_hid_release(uint32_t usage);
bool zmk_hid_is_pressed(uint32_t usage);
+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);
+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();
struct zmk_hid_consumer_report *zmk_hid_get_consumer_report();
+struct zmk_hid_mouse_report *zmk_hid_get_mouse_report();
diff --git a/app/include/zmk/hog.h b/app/include/zmk/hog.h
index 7523fb661a..9debc3ff31 100644
--- a/app/include/zmk/hog.h
+++ b/app/include/zmk/hog.h
@@ -13,3 +13,4 @@ int zmk_hog_init();
int zmk_hog_send_keyboard_report(struct zmk_hid_keyboard_report_body *body);
int zmk_hog_send_consumer_report(struct zmk_hid_consumer_report_body *body);
+int zmk_hog_send_mouse_report(struct zmk_hid_mouse_report_body *body);
diff --git a/app/include/zmk/mouse.h b/app/include/zmk/mouse.h
new file mode 100644
index 0000000000..f8f857441e
--- /dev/null
+++ b/app/include/zmk/mouse.h
@@ -0,0 +1,30 @@
+/*
+ * Copyright (c) 2021 The ZMK Contributors
+ *
+ * SPDX-License-Identifier: MIT
+ */
+
+#pragma once
+
+#include <zephyr.h>
+#include <dt-bindings/zmk/mouse.h>
+
+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
+ int acceleration_exponent;
+};
+
+struct vector2d {
+ float x;
+ float y;
+};
+
+struct k_work_q *zmk_mouse_work_q();
+int zmk_mouse_init();
\ No newline at end of file
diff --git a/app/src/behaviors/behavior_mouse_key_press.c b/app/src/behaviors/behavior_mouse_key_press.c
new file mode 100644
index 0000000000..e5f2709cbf
--- /dev/null
+++ b/app/src/behaviors/behavior_mouse_key_press.c
@@ -0,0 +1,48 @@
+/*
+ * Copyright (c) 2021 The ZMK Contributors
+ *
+ * SPDX-License-Identifier: MIT
+ */
+
+#define DT_DRV_COMPAT zmk_behavior_mouse_key_press
+
+#include <device.h>
+#include <drivers/behavior.h>
+#include <logging/log.h>
+
+#include <zmk/behavior.h>
+#include <zmk/event_manager.h>
+#include <zmk/events/mouse_button_state_changed.h>
+
+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);
+
+ 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);
+ 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 = {
+ .binding_pressed = on_keymap_binding_pressed, .binding_released = on_keymap_binding_released};
+
+#define KP_INST(n) \
+ DEVICE_DT_INST_DEFINE(n, behavior_mouse_key_press_init, device_pm_control_nop, NULL, NULL, \
+ 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
new file mode 100644
index 0000000000..5977a039d1
--- /dev/null
+++ b/app/src/behaviors/behavior_mouse_move.c
@@ -0,0 +1,57 @@
+/*
+ * Copyright (c) 2021 The ZMK Contributors
+ *
+ * SPDX-License-Identifier: MIT
+ */
+
+#define DT_DRV_COMPAT zmk_behavior_mouse_move
+
+#include <device.h>
+#include <drivers/behavior.h>
+#include <logging/log.h>
+
+#include <zmk/behavior.h>
+#include <zmk/event_manager.h>
+#include <zmk/events/mouse_move_state_changed.h>
+#include <zmk/mouse.h>
+
+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);
+ 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);
+ 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 = DT_INST_PROP(n, acceleration_exponent), \
+ }; \
+ DEVICE_DT_INST_DEFINE(n, behavior_mouse_move_init, device_pm_control_nop, NULL, \
+ &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 0000000000..6416235265
--- /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 <device.h>
+#include <drivers/behavior.h>
+#include <logging/log.h>
+
+#include <zmk/event_manager.h>
+#include <zmk/events/mouse_scroll_state_changed.h>
+#include <zmk/behavior.h>
+#include <zmk/hid.h>
+#include <zmk/endpoints.h>
+
+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 = DT_INST_PROP(n, acceleration_exponent), \
+ }; \
+ DEVICE_DT_INST_DEFINE(n, behavior_mouse_scroll_init, device_pm_control_nop, 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/endpoints.c b/app/src/endpoints.c
index 3376001060..0728fff3bd 100644
--- a/app/src/endpoints.c
+++ b/app/src/endpoints.c
@@ -144,6 +144,40 @@ int zmk_endpoints_send_report(uint16_t usage_page) {
}
}
+int zmk_endpoints_send_mouse_report() {
+ struct zmk_hid_mouse_report *mouse_report = zmk_hid_get_mouse_report();
+
+ switch (current_endpoint) {
+#if IS_ENABLED(CONFIG_ZMK_USB)
+ case ZMK_ENDPOINT_USB: {
+ int err = zmk_usb_hid_send_report((uint8_t *)mouse_report, sizeof(*mouse_report));
+ if (err) {
+ LOG_ERR("FAILED TO SEND OVER USB: %d", err);
+ }
+ return err;
+ }
+#endif /* IS_ENABLED(CONFIG_ZMK_USB) */
+
+#if IS_ENABLED(CONFIG_ZMK_BLE)
+ case ZMK_ENDPOINT_BLE: {
+#if IS_ENABLED(CONFIG_ZMK_MOUSE_WORK_QUEUE_DEDICATED)
+ int err = zmk_hog_send_mouse_report_direct(&mouse_report->body);
+#else
+ int err = zmk_hog_send_mouse_report(&mouse_report->body);
+#endif
+ if (err) {
+ LOG_ERR("FAILED TO SEND OVER HOG: %d", err);
+ }
+ return err;
+ }
+#endif /* IS_ENABLED(CONFIG_ZMK_BLE) */
+
+ default:
+ LOG_ERR("Unsupported endpoint %d", current_endpoint);
+ return -ENOTSUP;
+ }
+}
+
#if IS_ENABLED(CONFIG_SETTINGS)
static int endpoints_handle_set(const char *name, size_t len, settings_read_cb read_cb,
@@ -228,6 +262,7 @@ static enum zmk_endpoint get_selected_endpoint() {
static void disconnect_current_endpoint() {
zmk_hid_keyboard_clear();
zmk_hid_consumer_clear();
+ zmk_hid_mouse_clear();
zmk_endpoints_send_report(HID_USAGE_KEY);
zmk_endpoints_send_report(HID_USAGE_CONSUMER);
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 0000000000..e1ede41421
--- /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 <kernel.h>
+#include <zmk/events/mouse_button_state_changed.h>
+
+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 0000000000..faf89cb8ab
--- /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 <kernel.h>
+#include <zmk/events/mouse_move_state_changed.h>
+
+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 0000000000..4b4170fe00
--- /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 <kernel.h>
+#include <zmk/events/mouse_scroll_state_changed.h>
+
+ZMK_EVENT_IMPL(zmk_mouse_scroll_state_changed);
diff --git a/app/src/events/mouse_tick.c b/app/src/events/mouse_tick.c
new file mode 100644
index 0000000000..0930b9fb90
--- /dev/null
+++ b/app/src/events/mouse_tick.c
@@ -0,0 +1,10 @@
+/*
+ * Copyright (c) 2020 The ZMK Contributors
+ *
+ * SPDX-License-Identifier: MIT
+ */
+
+#include <kernel.h>
+#include <zmk/events/mouse_tick.h>
+
+ZMK_EVENT_IMPL(zmk_mouse_tick);
diff --git a/app/src/hid.c b/app/src/hid.c
index c3462ddeb7..9e7451b7f4 100644
--- a/app/src/hid.c
+++ b/app/src/hid.c
@@ -16,6 +16,9 @@ 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, .scroll_x = 0, .scroll_y = 0}};
+
// Keep track of how often a modifier was pressed.
// Only release the modifier if the count is 0.
static int explicit_modifier_counts[8] = {0, 0, 0, 0, 0, 0, 0, 0};
@@ -246,6 +249,85 @@ bool zmk_hid_is_pressed(uint32_t usage) {
return false;
}
+// Keep track of how often a button was pressed.
+// 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;
+
+#define SET_MOUSE_BUTTONS(btns) \
+ { \
+ mouse_report.body.buttons = btns; \
+ LOG_DBG("Mouse buttons set to 0x%02X", mouse_report.body.buttons); \
+ }
+
+int zmk_hid_mouse_button_press(zmk_mouse_button_t button) {
+ explicit_button_counts[button]++;
+ LOG_DBG("Button %d count %d", button, explicit_button_counts[button]);
+ WRITE_BIT(explicit_buttons, button, true);
+ SET_MOUSE_BUTTONS(explicit_buttons);
+ return 0;
+}
+
+int zmk_hid_mouse_button_release(zmk_mouse_button_t button) {
+ if (explicit_button_counts[button] <= 0) {
+ LOG_ERR("Tried to release button %d too often", button);
+ return -EINVAL;
+ }
+ explicit_button_counts[button]--;
+ LOG_DBG("Button %d count: %d", button, explicit_button_counts[button]);
+ if (explicit_button_counts[button] == 0) {
+ LOG_DBG("Button %d released", button);
+ WRITE_BIT(explicit_buttons, button, false);
+ }
+ SET_MOUSE_BUTTONS(explicit_buttons);
+ return 0;
+}
+
+int zmk_hid_mouse_buttons_press(zmk_mouse_button_flags_t buttons) {
+ for (zmk_mod_t i = 0; i < 16; i++) {
+ if (buttons & (1 << i)) {
+ zmk_hid_mouse_button_press(i);
+ }
+ }
+ return 0;
+}
+
+int zmk_hid_mouse_buttons_release(zmk_mouse_button_flags_t buttons) {
+ for (zmk_mod_t i = 0; i < 16; i++) {
+ if (buttons & (1 << i)) {
+ zmk_hid_mouse_button_release(i);
+ }
+ }
+ 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);
+}
+
+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);
+}
+
+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);
+}
+
+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() {
return &keyboard_report;
}
@@ -253,3 +335,7 @@ struct zmk_hid_keyboard_report *zmk_hid_get_keyboard_report() {
struct zmk_hid_consumer_report *zmk_hid_get_consumer_report() {
return &consumer_report;
}
+
+struct zmk_hid_mouse_report *zmk_hid_get_mouse_report() {
+ return &mouse_report;
+}
diff --git a/app/src/hid_listener.c b/app/src/hid_listener.c
index e233b0b8ed..8cde3a4323 100644
--- a/app/src/hid_listener.c
+++ b/app/src/hid_listener.c
@@ -11,7 +11,6 @@ LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL);
#include <zmk/event_manager.h>
#include <zmk/events/keycode_state_changed.h>
-#include <zmk/events/modifiers_state_changed.h>
#include <zmk/hid.h>
#include <dt-bindings/zmk/hid_usage_pages.h>
#include <zmk/endpoints.h>
@@ -71,13 +70,14 @@ static int hid_listener_keycode_released(const struct zmk_keycode_state_changed
}
int hid_listener(const zmk_event_t *eh) {
- const struct zmk_keycode_state_changed *ev = as_zmk_keycode_state_changed(eh);
- if (ev) {
- if (ev->state) {
- hid_listener_keycode_pressed(ev);
+ const struct zmk_keycode_state_changed *kc_ev = as_zmk_keycode_state_changed(eh);
+ if (kc_ev) {
+ if (kc_ev->state) {
+ hid_listener_keycode_pressed(kc_ev);
} else {
- hid_listener_keycode_released(ev);
+ hid_listener_keycode_released(kc_ev);
}
+ return 0;
}
return 0;
}
diff --git a/app/src/hog.c b/app/src/hog.c
index 3dd3e874a5..f915d27a91 100644
--- a/app/src/hog.c
+++ b/app/src/hog.c
@@ -56,6 +56,11 @@ static struct hids_report consumer_input = {
.type = HIDS_INPUT,
};
+static struct hids_report mouse_input = {
+ .id = 0x04,
+ .type = HIDS_INPUT,
+};
+
static bool host_requests_notification = false;
static uint8_t ctrl_point;
// static uint8_t proto_mode;
@@ -93,6 +98,13 @@ static ssize_t read_hids_consumer_input_report(struct bt_conn *conn,
sizeof(struct zmk_hid_consumer_report_body));
}
+static ssize_t read_hids_mouse_input_report(struct bt_conn *conn, const struct bt_gatt_attr *attr,
+ void *buf, uint16_t len, uint16_t offset) {
+ struct zmk_hid_mouse_report_body *report_body = &zmk_hid_get_mouse_report()->body;
+ return bt_gatt_attr_read(conn, attr, buf, len, offset, report_body,
+ sizeof(struct zmk_hid_mouse_report_body));
+}
+
// static ssize_t write_proto_mode(struct bt_conn *conn,
// const struct bt_gatt_attr *attr,
// const void *buf, uint16_t len, uint16_t offset,
@@ -139,6 +151,13 @@ BT_GATT_SERVICE_DEFINE(
BT_GATT_CCC(input_ccc_changed, BT_GATT_PERM_READ_ENCRYPT | BT_GATT_PERM_WRITE_ENCRYPT),
BT_GATT_DESCRIPTOR(BT_UUID_HIDS_REPORT_REF, BT_GATT_PERM_READ_ENCRYPT, read_hids_report_ref,
NULL, &consumer_input),
+
+ BT_GATT_CHARACTERISTIC(BT_UUID_HIDS_REPORT, BT_GATT_CHRC_READ | BT_GATT_CHRC_NOTIFY,
+ BT_GATT_PERM_READ_ENCRYPT, read_hids_mouse_input_report, NULL, NULL),
+ BT_GATT_CCC(input_ccc_changed, BT_GATT_PERM_READ_ENCRYPT | BT_GATT_PERM_WRITE_ENCRYPT),
+ BT_GATT_DESCRIPTOR(BT_UUID_HIDS_REPORT_REF, BT_GATT_PERM_READ_ENCRYPT, read_hids_report_ref,
+ NULL, &mouse_input),
+
BT_GATT_CHARACTERISTIC(BT_UUID_HIDS_CTRL_POINT, BT_GATT_CHRC_WRITE_WITHOUT_RESP,
BT_GATT_PERM_WRITE, NULL, write_ctrl_point, &ctrl_point));
@@ -261,6 +280,76 @@ int zmk_hog_send_consumer_report(struct zmk_hid_consumer_report_body *report) {
return 0;
};
+K_MSGQ_DEFINE(zmk_hog_mouse_msgq, sizeof(struct zmk_hid_mouse_report_body),
+ CONFIG_ZMK_BLE_MOUSE_REPORT_QUEUE_SIZE, 4);
+
+void send_mouse_report_callback(struct k_work *work) {
+ struct zmk_hid_mouse_report_body report;
+ while (k_msgq_get(&zmk_hog_mouse_msgq, &report, K_NO_WAIT) == 0) {
+ struct bt_conn *conn = destination_connection();
+ if (conn == NULL) {
+ return;
+ }
+
+ struct bt_gatt_notify_params notify_params = {
+ .attr = &hog_svc.attrs[13],
+ .data = &report,
+ .len = sizeof(report),
+ };
+
+ int err = bt_gatt_notify_cb(conn, &notify_params);
+ if (err) {
+ LOG_DBG("Error notifying %d", err);
+ }
+
+ bt_conn_unref(conn);
+ }
+};
+
+K_WORK_DEFINE(hog_mouse_work, send_mouse_report_callback);
+
+int zmk_hog_send_mouse_report(struct zmk_hid_mouse_report_body *report) {
+ int err = k_msgq_put(&zmk_hog_mouse_msgq, report, K_NO_WAIT);
+ if (err) {
+ switch (err) {
+ case -EAGAIN: {
+ LOG_WRN("Mouse message queue full, dropping report");
+ return err;
+ }
+ default:
+ LOG_WRN("Failed to queue mouse report to send (%d)", err);
+ return err;
+ }
+ }
+
+ k_work_submit_to_queue(&hog_work_q, &hog_mouse_work);
+
+ return 0;
+};
+
+int zmk_hog_send_mouse_report_direct(struct zmk_hid_mouse_report_body *report) {
+ struct bt_conn *conn = destination_connection();
+ if (conn == NULL) {
+ return 1;
+ }
+
+ struct bt_gatt_notify_params notify_params = {
+ .attr = &hog_svc.attrs[13],
+ .data = report,
+ .len = sizeof(*report),
+ };
+
+ int err = bt_gatt_notify_cb(conn, &notify_params);
+ if (err) {
+ LOG_DBG("Error notifying %d", err);
+ return err;
+ }
+
+ bt_conn_unref(conn);
+
+ return 0;
+};
+
int zmk_hog_init(const struct device *_arg) {
static const struct k_work_queue_config queue_config = {.name = "HID Over GATT Send Work"};
k_work_queue_start(&hog_work_q, hog_q_stack, K_THREAD_STACK_SIZEOF(hog_q_stack),
diff --git a/app/src/main.c b/app/src/main.c
index ae604a7b9e..d3b3e578b3 100644
--- a/app/src/main.c
+++ b/app/src/main.c
@@ -17,6 +17,10 @@ LOG_MODULE_REGISTER(zmk, CONFIG_ZMK_LOG_LEVEL);
#include <zmk/display.h>
#include <drivers/ext_power.h>
+#ifdef CONFIG_ZMK_MOUSE
+#include <zmk/mouse.h>
+#endif /* CONFIG_ZMK_MOUSE */
+
#define ZMK_KSCAN_DEV DT_LABEL(ZMK_MATRIX_NODE_ID)
void main(void) {
@@ -29,4 +33,8 @@ void main(void) {
#ifdef CONFIG_ZMK_DISPLAY
zmk_display_init();
#endif /* CONFIG_ZMK_DISPLAY */
+
+#ifdef CONFIG_ZMK_MOUSE
+ zmk_mouse_init();
+#endif /* CONFIG_ZMK_MOUSE */
}
diff --git a/app/src/mouse/Kconfig b/app/src/mouse/Kconfig
new file mode 100644
index 0000000000..1161b86b42
--- /dev/null
+++ b/app/src/mouse/Kconfig
@@ -0,0 +1,38 @@
+# Copyright (c) 2021 The ZMK Contributors
+# SPDX-License-Identifier: MIT
+
+menuconfig ZMK_MOUSE
+ bool "Enable ZMK mouse emulation"
+ default n
+
+config ZMK_MOUSE_TICK_DURATION
+ int "Mouse tick duration in ms"
+ default 8
+
+if ZMK_MOUSE
+
+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
diff --git a/app/src/mouse/key_listener.c b/app/src/mouse/key_listener.c
new file mode 100644
index 0000000000..713d032352
--- /dev/null
+++ b/app/src/mouse/key_listener.c
@@ -0,0 +1,160 @@
+/*
+ * Copyright (c) 2021 The ZMK Contributors
+ *
+ * SPDX-License-Identifier: MIT
+ */
+
+#include <drivers/behavior.h>
+#include <logging/log.h>
+
+LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL);
+
+#include <zmk/event_manager.h>
+#include <zmk/events/mouse_button_state_changed.h>
+#include <zmk/events/mouse_move_state_changed.h>
+#include <zmk/events/mouse_scroll_state_changed.h>
+#include <zmk/events/mouse_tick.h>
+#include <zmk/hid.h>
+#include <zmk/endpoints.h>
+#include <zmk/mouse.h>
+
+static struct vector2d move_speed = {0};
+static struct vector2d scroll_speed = {0};
+static struct mouse_config move_config = (struct mouse_config){0};
+static struct mouse_config scroll_config = (struct mouse_config){0};
+static int64_t start_time = 0;
+
+bool equals(const struct mouse_config *one, const struct mouse_config *other) {
+ return one->delay_ms == other->delay_ms &&
+ one->time_to_max_speed_ms == other->time_to_max_speed_ms &&
+ one->acceleration_exponent == other->acceleration_exponent;
+}
+
+static void clear_mouse_state(struct k_work *work) {
+ move_speed = (struct vector2d){0};
+ scroll_speed = (struct vector2d){0};
+ start_time = 0;
+ zmk_hid_mouse_movement_set(0, 0);
+ zmk_hid_mouse_scroll_set(0, 0);
+ LOG_DBG("Clearing state");
+}
+
+K_WORK_DEFINE(mouse_clear, &clear_mouse_state);
+
+void mouse_clear_cb(struct k_timer *dummy) {
+ k_work_submit_to_queue(zmk_mouse_work_q(), &mouse_clear);
+}
+
+static void mouse_tick_timer_handler(struct k_work *work) {
+ zmk_hid_mouse_movement_set(0, 0);
+ zmk_hid_mouse_scroll_set(0, 0);
+ LOG_DBG("Raising mouse tick event");
+ ZMK_EVENT_RAISE(
+ zmk_mouse_tick(move_speed, scroll_speed, move_config, scroll_config, &start_time));
+ zmk_endpoints_send_mouse_report();
+}
+
+K_WORK_DEFINE(mouse_tick, &mouse_tick_timer_handler);
+
+void mouse_timer_cb(struct k_timer *dummy) {
+ LOG_DBG("Submitting mouse work to queue");
+ k_work_submit_to_queue(zmk_mouse_work_q(), &mouse_tick);
+}
+
+K_TIMER_DEFINE(mouse_timer, mouse_timer_cb, mouse_clear_cb);
+
+static int mouse_timer_ref_count = 0;
+
+void mouse_timer_ref() {
+ if (mouse_timer_ref_count == 0) {
+ start_time = k_uptime_get();
+ k_timer_start(&mouse_timer, K_NO_WAIT, K_MSEC(CONFIG_ZMK_MOUSE_TICK_DURATION));
+ }
+ mouse_timer_ref_count += 1;
+}
+
+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);
+ }
+}
+
+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 (!equals(&move_config, &(mmv_ev->config)))
+ move_config = mmv_ev->config;
+
+ 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 (!equals(&scroll_config, &(msc_ev->config)))
+ scroll_config = msc_ev->config;
+ 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/main.c b/app/src/mouse/main.c
new file mode 100644
index 0000000000..49208a76ef
--- /dev/null
+++ b/app/src/mouse/main.c
@@ -0,0 +1,30 @@
+/*
+ * Copyright (c) 2020 The ZMK Contributors
+ *
+ * SPDX-License-Identifier: MIT
+ */
+
+#include <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() {
+#if IS_ENABLED(CONFIG_ZMK_MOUSE_WORK_QUEUE_DEDICATED)
+ k_work_q_start(&mouse_work_q, mouse_work_stack_area,
+ K_THREAD_STACK_SIZEOF(mouse_work_stack_area),
+ CONFIG_ZMK_MOUSE_DEDICATED_THREAD_PRIORITY);
+#endif
+ return 0;
+}
\ No newline at end of file
diff --git a/app/src/mouse/tick_listener.c b/app/src/mouse/tick_listener.c
new file mode 100644
index 0000000000..9c76bd5d2a
--- /dev/null
+++ b/app/src/mouse/tick_listener.c
@@ -0,0 +1,102 @@
+/*
+ * Copyright (c) 2020 The ZMK Contributors
+ *
+ * SPDX-License-Identifier: MIT
+ */
+
+#include <logging/log.h>
+
+LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL);
+
+#include <zmk/event_manager.h>
+#include <zmk/events/mouse_tick.h>
+#include <zmk/endpoints.h>
+#include <zmk/mouse.h>
+
+#include <sys/util.h> // CLAMP
+
+#if CONFIG_MINIMAL_LIBC
+static float powf(float base, float exponent) {
+ // poor man's power implementation rounds the exponent down to the nearest integer.
+ float power = 1.0f;
+ for (; exponent >= 1.0f; exponent--) {
+ power = power * base;
+ }
+ return power;
+}
+#else
+#include <math.h>
+#endif
+
+struct vector2d move_remainder = {0};
+struct vector2d scroll_remainder = {0};
+
+static int64_t ms_since_start(int64_t start, int64_t now, int64_t delay) {
+ int64_t move_duration = now - (start + delay);
+ // start can be in the future if there's a delay
+ if (move_duration < 0) {
+ move_duration = 0;
+ }
+ return move_duration;
+}
+
+static float speed(const 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 || config->time_to_max_speed_ms == 0 ||
+ config->acceleration_exponent == 0) {
+ 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 vector2d *remainder,
+ const struct mouse_config *config, struct vector2d max_speed,
+ int64_t now, int64_t *start_time) {
+ struct vector2d move = {0};
+ if (max_speed.x == 0 && max_speed.y == 0) {
+ *remainder = (struct vector2d){0};
+ return move;
+ }
+
+ int64_t move_duration = ms_since_start(*start_time, now, config->delay_ms);
+ move = (struct vector2d){
+ .x = speed(config, max_speed.x, move_duration) * CONFIG_ZMK_MOUSE_TICK_DURATION / 1000,
+ .y = speed(config, max_speed.y, move_duration) * CONFIG_ZMK_MOUSE_TICK_DURATION / 1000,
+ };
+
+ track_remainder(&(move.x), &(remainder->x));
+ track_remainder(&(move.y), &(remainder->y));
+
+ return move;
+}
+
+static void mouse_tick_handler(const struct zmk_mouse_tick *tick) {
+ struct vector2d move = update_movement(&move_remainder, &(tick->move_config), tick->max_move,
+ tick->timestamp, tick->start_time);
+ 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_remainder, &(tick->scroll_config),
+ tick->max_scroll, tick->timestamp, tick->start_time);
+ 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 0000000000..833100f6ac
--- /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 0000000000..259501ba3d
--- /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 0000000000..ecf06601c0
--- /dev/null
+++ b/app/tests/mouse-keys/mmv/native_posix.keymap
@@ -0,0 +1,26 @@
+#include <behaviors.dtsi>
+#include <dt-bindings/zmk/keys.h>
+#include <dt-bindings/zmk/kscan_mock.h>
+#include <dt-bindings/zmk/mouse.h>
+
+/ {
+ 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
new file mode 100644
index 0000000000..efe095e7a0
--- /dev/null
+++ b/docs/docs/behaviors/mouse-emulation.md
@@ -0,0 +1,110 @@
+---
+title: Mouse Emulation Behaviors
+sidebar_label: Mouse Emulation
+---
+
+## Summary
+
+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.
+
+## Configuration options
+
+This feature should be enabled via a config option:
+
+```
+CONFIG_ZMK_MOUSE=y
+```
+
+This option enables several others.
+
+### Dedicated thread processing
+
+`CONFIG_ZMK_MOUSE_WORK_QUEUE_DEDICATED` is enabled by default and separates the processing of mouse signals into a dedicated thread, significantly improving performance.
+
+### Tick rate configuration
+
+`CONFIG_ZMK_MOUSE_TICK_DURATION` sets the tick rate for mouse polling. It is set to 8 ms. by default.
+
+## Keycode Defines
+
+To make it easier to encode the HID keycode numeric values, most keymaps include
+the [`dt-bindings/zmk/mouse.h`](https://github.com/zmkfirmware/zmk/blob/main/app/include/dt-bindings/zmk/mouse.h) header
+provided by ZMK near the top:
+
+```
+#include <dt-bindings/zmk/mouse.h>
+```
+
+Doing so allows using a set of defines such as `MOVE_UP`, `MOVE_DOWN`, `LCLK` and `SCROLL_UP` with these behaviors.
+
+## Mouse Button Press
+
+This behavior can press/release up to 16 mouse buttons.
+
+### Behavior Binding
+
+- Reference: `&mkp`
+- Parameter: A `uint16` with each bit referring to a button.
+
+Example:
+
+```
+&mkp LCLK
+```
+
+## Mouse Movement
+
+This behavior is used to manipulate the cursor.
+
+### Behavior Binding
+
+- Reference: `&mmv`
+- Parameter: A `uint32` with the first 16 bits relating to horizontal movement
+ and the last 16 - to vertical movement.
+
+Example:
+
+```
+&mmv MOVE_UP
+```
+
+## Mouse Scrolling
+
+This behaviour is used to scroll, both horizontally and vertically.
+
+### Behavior Binding
+
+- Reference: `&mwh`
+- Parameter: A `uint16` with the first 8 bits relating to horizontal movement
+ and the last 8 - to vertical movement.
+
+Example:
+
+```
+&mwh SCROLL_UP
+```
+
+## Acceleration
+
+Both mouse movement and scrolling have independently configurable acceleration profiles with three parameters: delay before movement, time to max speed and the acceleration exponent.
+The exponent is usually set to 0 for constant speed, 1 for uniform acceleration or 2 for uniform jerk.
+
+These profiles can be configured inside your keymap:
+
+```
+&mmv {
+ time-to-max-speed-ms = <500>;
+};
+
+&mwh {
+ acceleration-exponent=<1>;
+};
+
+/ {
+ keymap {
+ ...
+ };
+};
+```
diff --git a/docs/sidebars.js b/docs/sidebars.js
index 7b445a2901..5e18b05dd0 100644
--- a/docs/sidebars.js
+++ b/docs/sidebars.js
@@ -34,6 +34,7 @@ module.exports = {
"behaviors/tap-dance",
"behaviors/caps-word",
"behaviors/key-repeat",
+ "behaviors/mouse-emulation",
"behaviors/reset",
"behaviors/bluetooth",
"behaviors/outputs",
From f8a0c397e25cb4a4c8c1a2d91da2db0e9401ad73 Mon Sep 17 00:00:00 2001
From: Jesper Jensen <jesper@jnsn.dev>
Date: Wed, 17 Aug 2022 23:26:23 +0200
Subject: [PATCH 2/2] Remove everything not required for buttons
I've stripped out everything not strictly required for mouse button HID
support. This include dropping the worker thread completely, shaving
down the USB HID descriptor. Flattening the mouse/ source directory
structure and removing a bunch of event handling.
I have kept the mouse event handling separate from the other HID event
handling since I figured that was a pretty neat split. If that's a bad
idea, do tell.
I've also added a test case for mouse button emulation, since that was
untested before.
The changes have been tested on a corne (split) in usb mode. Bindings on
both the left and the right side works (with the left side as master).
---
app/CMakeLists.txt | 9 +-
app/Kconfig | 4 +-
app/dts/behaviors.dtsi | 2 -
app/dts/behaviors/mouse_move.dtsi | 12 --
app/dts/behaviors/mouse_scroll.dtsi | 12 --
.../behaviors/zmk,behavior-mouse-move.yaml | 13 --
.../behaviors/zmk,behavior-mouse-scroll.yaml | 13 --
app/include/dt-bindings/zmk/mouse.h | 26 ---
.../zmk/events/mouse_button_state_changed.h | 2 +-
.../zmk/events/mouse_move_state_changed.h | 33 ----
.../zmk/events/mouse_scroll_state_changed.h | 34 ----
app/include/zmk/events/mouse_tick.h | 39 -----
app/include/zmk/hid.h | 135 ++-------------
app/include/zmk/mouse.h | 17 --
app/src/behaviors/behavior_mouse_move.c | 57 -------
app/src/behaviors/behavior_mouse_scroll.c | 58 -------
app/src/endpoints.c | 4 -
app/src/events/mouse_move_state_changed.c | 10 --
app/src/events/mouse_scroll_state_changed.c | 10 --
app/src/events/mouse_tick.c | 10 --
app/src/hid.c | 28 +--
app/src/hid_listener.c | 12 +-
app/src/hog.c | 21 ---
app/src/main.c | 8 -
app/src/mouse.c | 43 +++++
app/src/mouse/Kconfig | 38 -----
app/src/mouse/key_listener.c | 160 ------------------
app/src/mouse/main.c | 30 ----
app/src/mouse/tick_listener.c | 102 -----------
app/tests/mouse-keys/mkp/events.patterns | 1 +
.../mouse-keys/mkp/keycode_events.snapshot | 10 ++
.../{mmv => mkp}/native_posix.keymap | 12 +-
.../mouse-keys/mkp/native_posix_64.keymap | 28 +++
app/tests/mouse-keys/mmv/events.patterns | 1 -
.../mouse-keys/mmv/keycode_events.snapshot | 2 -
docs/docs/behaviors/mouse-emulation.md | 67 +-------
36 files changed, 120 insertions(+), 943 deletions(-)
delete mode 100644 app/dts/behaviors/mouse_move.dtsi
delete mode 100644 app/dts/behaviors/mouse_scroll.dtsi
delete mode 100644 app/dts/bindings/behaviors/zmk,behavior-mouse-move.yaml
delete mode 100644 app/dts/bindings/behaviors/zmk,behavior-mouse-scroll.yaml
delete mode 100644 app/include/zmk/events/mouse_move_state_changed.h
delete mode 100644 app/include/zmk/events/mouse_scroll_state_changed.h
delete mode 100644 app/include/zmk/events/mouse_tick.h
delete mode 100644 app/src/behaviors/behavior_mouse_move.c
delete mode 100644 app/src/behaviors/behavior_mouse_scroll.c
delete mode 100644 app/src/events/mouse_move_state_changed.c
delete mode 100644 app/src/events/mouse_scroll_state_changed.c
delete mode 100644 app/src/events/mouse_tick.c
create mode 100644 app/src/mouse.c
delete mode 100644 app/src/mouse/Kconfig
delete mode 100644 app/src/mouse/key_listener.c
delete mode 100644 app/src/mouse/main.c
delete mode 100644 app/src/mouse/tick_listener.c
create mode 100644 app/tests/mouse-keys/mkp/events.patterns
create mode 100644 app/tests/mouse-keys/mkp/keycode_events.snapshot
rename app/tests/mouse-keys/{mmv => mkp}/native_posix.keymap (64%)
create mode 100644 app/tests/mouse-keys/mkp/native_posix_64.keymap
delete mode 100644 app/tests/mouse-keys/mmv/events.patterns
delete mode 100644 app/tests/mouse-keys/mmv/keycode_events.snapshot
diff --git a/app/CMakeLists.txt b/app/CMakeLists.txt
index 351505ad77..cf18f4c029 100644
--- a/app/CMakeLists.txt
+++ b/app/CMakeLists.txt
@@ -24,9 +24,6 @@ target_sources(app PRIVATE src/stdlib.c)
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/mouse/key_listener.c)
-target_sources(app PRIVATE src/mouse/main.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)
@@ -35,15 +32,13 @@ target_sources(app PRIVATE src/events/activity_state_changed.c)
target_sources(app PRIVATE src/events/position_state_changed.c)
target_sources(app PRIVATE src/events/sensor_event.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_USB_DEVICE_STACK app PRIVATE src/events/usb_conn_state_changed.c)
target_sources(app PRIVATE src/behaviors/behavior_reset.c)
target_sources_ifdef(CONFIG_ZMK_EXT_POWER app PRIVATE src/behaviors/behavior_ext_power.c)
if ((NOT CONFIG_ZMK_SPLIT) OR CONFIG_ZMK_SPLIT_ROLE_CENTRAL)
target_sources(app PRIVATE src/hid.c)
+ target_sources(app PRIVATE src/mouse.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(app PRIVATE src/behaviors/behavior_hold_tap.c)
@@ -61,8 +56,6 @@ if ((NOT CONFIG_ZMK_SPLIT) OR CONFIG_ZMK_SPLIT_ROLE_CENTRAL)
target_sources(app PRIVATE src/behaviors/behavior_none.c)
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_scroll.c)
target_sources(app PRIVATE src/combo.c)
target_sources(app PRIVATE src/behavior_queue.c)
target_sources(app PRIVATE src/conditional_layer.c)
diff --git a/app/Kconfig b/app/Kconfig
index 3c59605814..7a422c2b9c 100644
--- a/app/Kconfig
+++ b/app/Kconfig
@@ -291,7 +291,9 @@ endmenu
menu "Mouse Options"
-rsource "src/mouse/Kconfig"
+config ZMK_MOUSE
+ bool "Enable ZMK mouse emulation"
+ default n
#Mouse Options
endmenu
diff --git a/app/dts/behaviors.dtsi b/app/dts/behaviors.dtsi
index 77eccf912f..23f2fee280 100644
--- a/app/dts/behaviors.dtsi
+++ b/app/dts/behaviors.dtsi
@@ -20,5 +20,3 @@
#include <behaviors/backlight.dtsi>
#include <behaviors/macros.dtsi>
#include <behaviors/mouse_key_press.dtsi>
-#include <behaviors/mouse_move.dtsi>
-#include <behaviors/mouse_scroll.dtsi>
diff --git a/app/dts/behaviors/mouse_move.dtsi b/app/dts/behaviors/mouse_move.dtsi
deleted file mode 100644
index d34329c806..0000000000
--- a/app/dts/behaviors/mouse_move.dtsi
+++ /dev/null
@@ -1,12 +0,0 @@
-/ {
- behaviors {
- /omit-if-no-ref/ mmv: behavior_mouse_move {
- compatible = "zmk,behavior-mouse-move";
- label = "MOUSE_MOVE";
- #binding-cells = <1>;
- delay-ms = <0>;
- time-to-max-speed-ms = <300>;
- acceleration-exponent = <1>;
- };
- };
-};
diff --git a/app/dts/behaviors/mouse_scroll.dtsi b/app/dts/behaviors/mouse_scroll.dtsi
deleted file mode 100644
index fb54886dcb..0000000000
--- a/app/dts/behaviors/mouse_scroll.dtsi
+++ /dev/null
@@ -1,12 +0,0 @@
-/ {
- 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 = <300>;
- acceleration-exponent = <0>;
- };
- };
-};
diff --git a/app/dts/bindings/behaviors/zmk,behavior-mouse-move.yaml b/app/dts/bindings/behaviors/zmk,behavior-mouse-move.yaml
deleted file mode 100644
index 73ec34ec2d..0000000000
--- a/app/dts/bindings/behaviors/zmk,behavior-mouse-move.yaml
+++ /dev/null
@@ -1,13 +0,0 @@
-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
deleted file mode 100644
index 5a932bc590..0000000000
--- a/app/dts/bindings/behaviors/zmk,behavior-mouse-scroll.yaml
+++ /dev/null
@@ -1,13 +0,0 @@
-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/include/dt-bindings/zmk/mouse.h b/app/include/dt-bindings/zmk/mouse.h
index cf0415c9b2..4bb3064dfc 100644
--- a/app/include/dt-bindings/zmk/mouse.h
+++ b/app/include/dt-bindings/zmk/mouse.h
@@ -27,29 +27,3 @@
#define MB7 (0x40)
#define MB8 (0x80)
-
-/* Mouse move behavior */
-#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))
-
-#define MOVE_UP MOVE_VERT(-600)
-#define MOVE_DOWN MOVE_VERT(600)
-#define MOVE_LEFT MOVE_HOR(-600)
-#define MOVE_RIGHT MOVE_HOR(600)
-
-/* 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 SCROLL(hor, vert) (SCROLL_HOR(hor) + SCROLL_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
index 7ec4d2087c..6c3adae30d 100644
--- a/app/include/zmk/events/mouse_button_state_changed.h
+++ b/app/include/zmk/events/mouse_button_state_changed.h
@@ -23,5 +23,5 @@ 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});
+ .buttons = ZMK_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
deleted file mode 100644
index 8866f81d4e..0000000000
--- a/app/include/zmk/events/mouse_move_state_changed.h
+++ /dev/null
@@ -1,33 +0,0 @@
-
-/*
- * Copyright (c) 2020 The ZMK Contributors
- *
- * SPDX-License-Identifier: MIT
- */
-
-#pragma once
-
-#include <zephyr.h>
-#include <zmk/event_manager.h>
-#include <zmk/mouse.h>
-
-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
deleted file mode 100644
index fa60e8a742..0000000000
--- a/app/include/zmk/events/mouse_scroll_state_changed.h
+++ /dev/null
@@ -1,34 +0,0 @@
-
-/*
- * Copyright (c) 2020 The ZMK Contributors
- *
- * SPDX-License-Identifier: MIT
- */
-
-#pragma once
-
-#include <zephyr.h>
-#include <zmk/event_manager.h>
-#include <zmk/mouse.h>
-#include <dt-bindings/zmk/mouse.h>
-
-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_tick.h b/app/include/zmk/events/mouse_tick.h
deleted file mode 100644
index c75b9b4f86..0000000000
--- a/app/include/zmk/events/mouse_tick.h
+++ /dev/null
@@ -1,39 +0,0 @@
-
-/*
- * Copyright (c) 2020 The ZMK Contributors
- *
- * SPDX-License-Identifier: MIT
- */
-
-#pragma once
-
-#include <dt-bindings/zmk/mouse.h>
-#include <zephyr.h>
-#include <zmk/event_manager.h>
-#include <zmk/mouse.h>
-
-struct zmk_mouse_tick {
- struct vector2d max_move;
- struct vector2d max_scroll;
- struct mouse_config move_config;
- struct mouse_config scroll_config;
- int64_t *start_time;
- 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,
- int64_t *movement_start) {
- 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,
- .start_time = movement_start,
- .timestamp = k_uptime_get(),
- });
-}
diff --git a/app/include/zmk/hid.h b/app/include/zmk/hid.h
index aa26cd3a54..f30db315d9 100644
--- a/app/include/zmk/hid.h
+++ b/app/include/zmk/hid.h
@@ -14,6 +14,8 @@
#include <dt-bindings/zmk/hid_usage.h>
#include <dt-bindings/zmk/hid_usage_pages.h>
+#define ZMK_HID_KEYBOARD_NKRO_MAX_USAGE HID_USAGE_KEY_KEYPAD_EQUAL
+
#define COLLECTION_REPORT 0x03
static const uint8_t zmk_hid_report_desc[] = {
@@ -89,115 +91,22 @@ static const uint8_t zmk_hid_report_desc[] = {
HID_INPUT(0x00),
HID_END_COLLECTION,
- /* USAGE_PAGE (Generic Desktop) */
- HID_GI_USAGE_PAGE,
- HID_USAGE_GD,
- /* USAGE (Mouse) */
- HID_LI_USAGE,
- HID_USAGE_GD_MOUSE,
- /* COLLECTION (Application) */
- HID_MI_COLLECTION,
- COLLECTION_APPLICATION,
- /* REPORT ID (4) */
- HID_GI_REPORT_ID,
- 0x04,
- /* USAGE (Pointer) */
- HID_LI_USAGE,
- HID_USAGE_GD_POINTER,
- /* COLLECTION (Physical) */
- HID_MI_COLLECTION,
- COLLECTION_PHYSICAL,
- /* USAGE_PAGE (Button) */
- HID_GI_USAGE_PAGE,
- HID_USAGE_BUTTON,
- /* USAGE_MINIMUM (0x1) (button 1?) */
- HID_LI_USAGE_MIN(1),
- 0x1,
- /* USAGE_MAXIMUM (0x10) (button 5? Buttons up to 8 still work) */
- HID_LI_USAGE_MAX(1),
- 0x10,
- /* LOGICAL_MINIMUM (0) */
- HID_GI_LOGICAL_MIN(1),
- 0x00,
- /* LOGICAL_MAXIMUM (1) */
- HID_GI_LOGICAL_MAX(1),
- 0x01,
- /* REPORT_SIZE (1) */
- HID_GI_REPORT_SIZE,
- 0x01,
- /* REPORT_COUNT (16) */
- HID_GI_REPORT_COUNT,
- 0x10,
- /* INPUT (Data,Var,Abs) */
- HID_MI_INPUT,
- 0x02,
- /* USAGE_PAGE (Generic Desktop) */
- HID_GI_USAGE_PAGE,
- HID_USAGE_GD,
- /* LOGICAL_MINIMUM (-32767) */
- HID_GI_LOGICAL_MIN(2),
- 0x01,
- 0x80,
- /* LOGICAL_MAXIMUM (32767) */
- HID_GI_LOGICAL_MAX(2),
- 0xFF,
- 0x7F,
- /* REPORT_SIZE (16) */
- HID_GI_REPORT_SIZE,
- 0x10,
- /* REPORT_COUNT (2) */
- HID_GI_REPORT_COUNT,
- 0x02,
- /* USAGE (X) */ // Vertical scroll
- HID_LI_USAGE,
- HID_USAGE_GD_X,
- /* USAGE (Y) */
- HID_LI_USAGE,
- HID_USAGE_GD_Y,
- /* Input (Data,Var,Rel) */
- HID_MI_INPUT,
- 0x06,
- /* LOGICAL_MINIMUM (-127) */
- HID_GI_LOGICAL_MIN(1),
- 0x81,
- /* LOGICAL_MAXIMUM (127) */
- HID_GI_LOGICAL_MAX(1),
- 0x7F,
- /* REPORT_SIZE (8) */
- HID_GI_REPORT_SIZE,
- 0x08,
- /* REPORT_COUNT (1) */
- HID_GI_REPORT_COUNT,
- 0x01,
- /* USAGE (Wheel) */
- HID_LI_USAGE,
- HID_USAGE_GD_WHEEL,
- /* Input (Data,Var,Rel) */
- HID_MI_INPUT,
- 0x06,
- /* USAGE_PAGE (Consumer) */ // Horizontal scroll
- HID_GI_USAGE_PAGE,
- HID_USAGE_CONSUMER,
- /* USAGE (AC Pan) */
- 0x0A,
- 0x38,
- 0x02,
- /* LOGICAL_MINIMUM (-127) */
- HID_GI_LOGICAL_MIN(1),
- 0x81,
- /* LOGICAL_MAXIMUM (127) */
- HID_GI_LOGICAL_MAX(1),
- 0x7F,
- /* REPORT_COUNT (1) */
- HID_GI_REPORT_COUNT,
- 0x01,
- /* Input (Data,Var,Rel) */
- HID_MI_INPUT,
- 0x06,
- /* END COLLECTION */
- HID_MI_COLLECTION_END,
- /* END COLLECTION */
- HID_MI_COLLECTION_END,
+ HID_USAGE_PAGE(HID_USAGE_GD),
+ HID_USAGE(HID_USAGE_GD_MOUSE),
+ HID_COLLECTION(HID_COLLECTION_APPLICATION),
+ HID_REPORT_ID(0x04),
+ HID_USAGE(HID_USAGE_GD_POINTER),
+ HID_COLLECTION(HID_COLLECTION_PHYSICAL),
+ HID_USAGE_PAGE(HID_USAGE_BUTTON),
+ HID_USAGE_MIN8(0x1),
+ HID_USAGE_MAX8(0x10),
+ HID_LOGICAL_MIN8(0x00),
+ HID_LOGICAL_MAX8(0x01),
+ HID_REPORT_SIZE(0x01),
+ HID_REPORT_COUNT(0x10),
+ HID_INPUT(0x02),
+ HID_END_COLLECTION,
+ HID_END_COLLECTION,
};
// struct zmk_hid_boot_report
@@ -237,10 +146,6 @@ struct zmk_hid_consumer_report {
struct zmk_hid_mouse_report_body {
zmk_mouse_button_flags_t buttons;
- int16_t x;
- int16_t y;
- int8_t scroll_y;
- int8_t scroll_x;
} __packed;
struct zmk_hid_mouse_report {
@@ -276,10 +181,6 @@ 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);
-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 f8f857441e..e749ac5d62 100644
--- a/app/include/zmk/mouse.h
+++ b/app/include/zmk/mouse.h
@@ -11,20 +11,3 @@
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
- int acceleration_exponent;
-};
-
-struct vector2d {
- float x;
- float y;
-};
-
-struct k_work_q *zmk_mouse_work_q();
-int zmk_mouse_init();
\ No newline at end of file
diff --git a/app/src/behaviors/behavior_mouse_move.c b/app/src/behaviors/behavior_mouse_move.c
deleted file mode 100644
index 5977a039d1..0000000000
--- a/app/src/behaviors/behavior_mouse_move.c
+++ /dev/null
@@ -1,57 +0,0 @@
-/*
- * Copyright (c) 2021 The ZMK Contributors
- *
- * SPDX-License-Identifier: MIT
- */
-
-#define DT_DRV_COMPAT zmk_behavior_mouse_move
-
-#include <device.h>
-#include <drivers/behavior.h>
-#include <logging/log.h>
-
-#include <zmk/behavior.h>
-#include <zmk/event_manager.h>
-#include <zmk/events/mouse_move_state_changed.h>
-#include <zmk/mouse.h>
-
-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);
- 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);
- 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 = DT_INST_PROP(n, acceleration_exponent), \
- }; \
- DEVICE_DT_INST_DEFINE(n, behavior_mouse_move_init, device_pm_control_nop, NULL, \
- &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
deleted file mode 100644
index 6416235265..0000000000
--- a/app/src/behaviors/behavior_mouse_scroll.c
+++ /dev/null
@@ -1,58 +0,0 @@
-/*
- * Copyright (c) 2021 The ZMK Contributors
- *
- * SPDX-License-Identifier: MIT
- */
-
-#define DT_DRV_COMPAT zmk_behavior_mouse_scroll
-
-#include <device.h>
-#include <drivers/behavior.h>
-#include <logging/log.h>
-
-#include <zmk/event_manager.h>
-#include <zmk/events/mouse_scroll_state_changed.h>
-#include <zmk/behavior.h>
-#include <zmk/hid.h>
-#include <zmk/endpoints.h>
-
-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 = DT_INST_PROP(n, acceleration_exponent), \
- }; \
- DEVICE_DT_INST_DEFINE(n, behavior_mouse_scroll_init, device_pm_control_nop, 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/endpoints.c b/app/src/endpoints.c
index 0728fff3bd..6984faa99f 100644
--- a/app/src/endpoints.c
+++ b/app/src/endpoints.c
@@ -160,11 +160,7 @@ int zmk_endpoints_send_mouse_report() {
#if IS_ENABLED(CONFIG_ZMK_BLE)
case ZMK_ENDPOINT_BLE: {
-#if IS_ENABLED(CONFIG_ZMK_MOUSE_WORK_QUEUE_DEDICATED)
- int err = zmk_hog_send_mouse_report_direct(&mouse_report->body);
-#else
int err = zmk_hog_send_mouse_report(&mouse_report->body);
-#endif
if (err) {
LOG_ERR("FAILED TO SEND OVER HOG: %d", err);
}
diff --git a/app/src/events/mouse_move_state_changed.c b/app/src/events/mouse_move_state_changed.c
deleted file mode 100644
index faf89cb8ab..0000000000
--- a/app/src/events/mouse_move_state_changed.c
+++ /dev/null
@@ -1,10 +0,0 @@
-/*
- * Copyright (c) 2020 The ZMK Contributors
- *
- * SPDX-License-Identifier: MIT
- */
-
-#include <kernel.h>
-#include <zmk/events/mouse_move_state_changed.h>
-
-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
deleted file mode 100644
index 4b4170fe00..0000000000
--- a/app/src/events/mouse_scroll_state_changed.c
+++ /dev/null
@@ -1,10 +0,0 @@
-/*
- * Copyright (c) 2020 The ZMK Contributors
- *
- * SPDX-License-Identifier: MIT
- */
-
-#include <kernel.h>
-#include <zmk/events/mouse_scroll_state_changed.h>
-
-ZMK_EVENT_IMPL(zmk_mouse_scroll_state_changed);
diff --git a/app/src/events/mouse_tick.c b/app/src/events/mouse_tick.c
deleted file mode 100644
index 0930b9fb90..0000000000
--- a/app/src/events/mouse_tick.c
+++ /dev/null
@@ -1,10 +0,0 @@
-/*
- * Copyright (c) 2020 The ZMK Contributors
- *
- * SPDX-License-Identifier: MIT
- */
-
-#include <kernel.h>
-#include <zmk/events/mouse_tick.h>
-
-ZMK_EVENT_IMPL(zmk_mouse_tick);
diff --git a/app/src/hid.c b/app/src/hid.c
index 9e7451b7f4..acd0a2b248 100644
--- a/app/src/hid.c
+++ b/app/src/hid.c
@@ -17,7 +17,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, .scroll_x = 0, .scroll_y = 0}};
+ .report_id = 4, .body = {.buttons = 0}};
// Keep track of how often a modifier was pressed.
// Only release the modifier if the count is 0.
@@ -300,32 +300,6 @@ int zmk_hid_mouse_buttons_release(zmk_mouse_button_flags_t buttons) {
}
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);
-}
-
-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);
-}
-
-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);
-}
-
-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 8cde3a4323..e233b0b8ed 100644
--- a/app/src/hid_listener.c
+++ b/app/src/hid_listener.c
@@ -11,6 +11,7 @@ LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL);
#include <zmk/event_manager.h>
#include <zmk/events/keycode_state_changed.h>
+#include <zmk/events/modifiers_state_changed.h>
#include <zmk/hid.h>
#include <dt-bindings/zmk/hid_usage_pages.h>
#include <zmk/endpoints.h>
@@ -70,14 +71,13 @@ static int hid_listener_keycode_released(const struct zmk_keycode_state_changed
}
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) {
- if (kc_ev->state) {
- hid_listener_keycode_pressed(kc_ev);
+ const struct zmk_keycode_state_changed *ev = as_zmk_keycode_state_changed(eh);
+ if (ev) {
+ if (ev->state) {
+ hid_listener_keycode_pressed(ev);
} else {
- hid_listener_keycode_released(kc_ev);
+ hid_listener_keycode_released(ev);
}
- return 0;
}
return 0;
}
diff --git a/app/src/hog.c b/app/src/hog.c
index f915d27a91..1733ffa45c 100644
--- a/app/src/hog.c
+++ b/app/src/hog.c
@@ -306,28 +306,7 @@ void send_mouse_report_callback(struct k_work *work) {
}
};
-K_WORK_DEFINE(hog_mouse_work, send_mouse_report_callback);
-
int zmk_hog_send_mouse_report(struct zmk_hid_mouse_report_body *report) {
- int err = k_msgq_put(&zmk_hog_mouse_msgq, report, K_NO_WAIT);
- if (err) {
- switch (err) {
- case -EAGAIN: {
- LOG_WRN("Mouse message queue full, dropping report");
- return err;
- }
- default:
- LOG_WRN("Failed to queue mouse report to send (%d)", err);
- return err;
- }
- }
-
- k_work_submit_to_queue(&hog_work_q, &hog_mouse_work);
-
- return 0;
-};
-
-int zmk_hog_send_mouse_report_direct(struct zmk_hid_mouse_report_body *report) {
struct bt_conn *conn = destination_connection();
if (conn == NULL) {
return 1;
diff --git a/app/src/main.c b/app/src/main.c
index d3b3e578b3..ae604a7b9e 100644
--- a/app/src/main.c
+++ b/app/src/main.c
@@ -17,10 +17,6 @@ LOG_MODULE_REGISTER(zmk, CONFIG_ZMK_LOG_LEVEL);
#include <zmk/display.h>
#include <drivers/ext_power.h>
-#ifdef CONFIG_ZMK_MOUSE
-#include <zmk/mouse.h>
-#endif /* CONFIG_ZMK_MOUSE */
-
#define ZMK_KSCAN_DEV DT_LABEL(ZMK_MATRIX_NODE_ID)
void main(void) {
@@ -33,8 +29,4 @@ void main(void) {
#ifdef CONFIG_ZMK_DISPLAY
zmk_display_init();
#endif /* CONFIG_ZMK_DISPLAY */
-
-#ifdef CONFIG_ZMK_MOUSE
- zmk_mouse_init();
-#endif /* CONFIG_ZMK_MOUSE */
}
diff --git a/app/src/mouse.c b/app/src/mouse.c
new file mode 100644
index 0000000000..a02d6dd0e9
--- /dev/null
+++ b/app/src/mouse.c
@@ -0,0 +1,43 @@
+/*
+ * Copyright (c) 2021 The ZMK Contributors
+ *
+ * SPDX-License-Identifier: MIT
+ */
+
+#include <drivers/behavior.h>
+#include <logging/log.h>
+
+LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL);
+
+#include <zmk/events/mouse_button_state_changed.h>
+#include <zmk/hid.h>
+#include <zmk/endpoints.h>
+#include <zmk/mouse.h>
+
+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_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);
diff --git a/app/src/mouse/Kconfig b/app/src/mouse/Kconfig
deleted file mode 100644
index 1161b86b42..0000000000
--- a/app/src/mouse/Kconfig
+++ /dev/null
@@ -1,38 +0,0 @@
-# Copyright (c) 2021 The ZMK Contributors
-# SPDX-License-Identifier: MIT
-
-menuconfig ZMK_MOUSE
- bool "Enable ZMK mouse emulation"
- default n
-
-config ZMK_MOUSE_TICK_DURATION
- int "Mouse tick duration in ms"
- default 8
-
-if ZMK_MOUSE
-
-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
diff --git a/app/src/mouse/key_listener.c b/app/src/mouse/key_listener.c
deleted file mode 100644
index 713d032352..0000000000
--- a/app/src/mouse/key_listener.c
+++ /dev/null
@@ -1,160 +0,0 @@
-/*
- * Copyright (c) 2021 The ZMK Contributors
- *
- * SPDX-License-Identifier: MIT
- */
-
-#include <drivers/behavior.h>
-#include <logging/log.h>
-
-LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL);
-
-#include <zmk/event_manager.h>
-#include <zmk/events/mouse_button_state_changed.h>
-#include <zmk/events/mouse_move_state_changed.h>
-#include <zmk/events/mouse_scroll_state_changed.h>
-#include <zmk/events/mouse_tick.h>
-#include <zmk/hid.h>
-#include <zmk/endpoints.h>
-#include <zmk/mouse.h>
-
-static struct vector2d move_speed = {0};
-static struct vector2d scroll_speed = {0};
-static struct mouse_config move_config = (struct mouse_config){0};
-static struct mouse_config scroll_config = (struct mouse_config){0};
-static int64_t start_time = 0;
-
-bool equals(const struct mouse_config *one, const struct mouse_config *other) {
- return one->delay_ms == other->delay_ms &&
- one->time_to_max_speed_ms == other->time_to_max_speed_ms &&
- one->acceleration_exponent == other->acceleration_exponent;
-}
-
-static void clear_mouse_state(struct k_work *work) {
- move_speed = (struct vector2d){0};
- scroll_speed = (struct vector2d){0};
- start_time = 0;
- zmk_hid_mouse_movement_set(0, 0);
- zmk_hid_mouse_scroll_set(0, 0);
- LOG_DBG("Clearing state");
-}
-
-K_WORK_DEFINE(mouse_clear, &clear_mouse_state);
-
-void mouse_clear_cb(struct k_timer *dummy) {
- k_work_submit_to_queue(zmk_mouse_work_q(), &mouse_clear);
-}
-
-static void mouse_tick_timer_handler(struct k_work *work) {
- zmk_hid_mouse_movement_set(0, 0);
- zmk_hid_mouse_scroll_set(0, 0);
- LOG_DBG("Raising mouse tick event");
- ZMK_EVENT_RAISE(
- zmk_mouse_tick(move_speed, scroll_speed, move_config, scroll_config, &start_time));
- zmk_endpoints_send_mouse_report();
-}
-
-K_WORK_DEFINE(mouse_tick, &mouse_tick_timer_handler);
-
-void mouse_timer_cb(struct k_timer *dummy) {
- LOG_DBG("Submitting mouse work to queue");
- k_work_submit_to_queue(zmk_mouse_work_q(), &mouse_tick);
-}
-
-K_TIMER_DEFINE(mouse_timer, mouse_timer_cb, mouse_clear_cb);
-
-static int mouse_timer_ref_count = 0;
-
-void mouse_timer_ref() {
- if (mouse_timer_ref_count == 0) {
- start_time = k_uptime_get();
- k_timer_start(&mouse_timer, K_NO_WAIT, K_MSEC(CONFIG_ZMK_MOUSE_TICK_DURATION));
- }
- mouse_timer_ref_count += 1;
-}
-
-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);
- }
-}
-
-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 (!equals(&move_config, &(mmv_ev->config)))
- move_config = mmv_ev->config;
-
- 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 (!equals(&scroll_config, &(msc_ev->config)))
- scroll_config = msc_ev->config;
- 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/main.c b/app/src/mouse/main.c
deleted file mode 100644
index 49208a76ef..0000000000
--- a/app/src/mouse/main.c
+++ /dev/null
@@ -1,30 +0,0 @@
-/*
- * Copyright (c) 2020 The ZMK Contributors
- *
- * SPDX-License-Identifier: MIT
- */
-
-#include <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() {
-#if IS_ENABLED(CONFIG_ZMK_MOUSE_WORK_QUEUE_DEDICATED)
- k_work_q_start(&mouse_work_q, mouse_work_stack_area,
- K_THREAD_STACK_SIZEOF(mouse_work_stack_area),
- CONFIG_ZMK_MOUSE_DEDICATED_THREAD_PRIORITY);
-#endif
- return 0;
-}
\ No newline at end of file
diff --git a/app/src/mouse/tick_listener.c b/app/src/mouse/tick_listener.c
deleted file mode 100644
index 9c76bd5d2a..0000000000
--- a/app/src/mouse/tick_listener.c
+++ /dev/null
@@ -1,102 +0,0 @@
-/*
- * Copyright (c) 2020 The ZMK Contributors
- *
- * SPDX-License-Identifier: MIT
- */
-
-#include <logging/log.h>
-
-LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL);
-
-#include <zmk/event_manager.h>
-#include <zmk/events/mouse_tick.h>
-#include <zmk/endpoints.h>
-#include <zmk/mouse.h>
-
-#include <sys/util.h> // CLAMP
-
-#if CONFIG_MINIMAL_LIBC
-static float powf(float base, float exponent) {
- // poor man's power implementation rounds the exponent down to the nearest integer.
- float power = 1.0f;
- for (; exponent >= 1.0f; exponent--) {
- power = power * base;
- }
- return power;
-}
-#else
-#include <math.h>
-#endif
-
-struct vector2d move_remainder = {0};
-struct vector2d scroll_remainder = {0};
-
-static int64_t ms_since_start(int64_t start, int64_t now, int64_t delay) {
- int64_t move_duration = now - (start + delay);
- // start can be in the future if there's a delay
- if (move_duration < 0) {
- move_duration = 0;
- }
- return move_duration;
-}
-
-static float speed(const 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 || config->time_to_max_speed_ms == 0 ||
- config->acceleration_exponent == 0) {
- 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 vector2d *remainder,
- const struct mouse_config *config, struct vector2d max_speed,
- int64_t now, int64_t *start_time) {
- struct vector2d move = {0};
- if (max_speed.x == 0 && max_speed.y == 0) {
- *remainder = (struct vector2d){0};
- return move;
- }
-
- int64_t move_duration = ms_since_start(*start_time, now, config->delay_ms);
- move = (struct vector2d){
- .x = speed(config, max_speed.x, move_duration) * CONFIG_ZMK_MOUSE_TICK_DURATION / 1000,
- .y = speed(config, max_speed.y, move_duration) * CONFIG_ZMK_MOUSE_TICK_DURATION / 1000,
- };
-
- track_remainder(&(move.x), &(remainder->x));
- track_remainder(&(move.y), &(remainder->y));
-
- return move;
-}
-
-static void mouse_tick_handler(const struct zmk_mouse_tick *tick) {
- struct vector2d move = update_movement(&move_remainder, &(tick->move_config), tick->max_move,
- tick->timestamp, tick->start_time);
- 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_remainder, &(tick->scroll_config),
- tick->max_scroll, tick->timestamp, tick->start_time);
- 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/mkp/events.patterns b/app/tests/mouse-keys/mkp/events.patterns
new file mode 100644
index 0000000000..2599345c2d
--- /dev/null
+++ b/app/tests/mouse-keys/mkp/events.patterns
@@ -0,0 +1 @@
+s/.*zmk_hid_mouse_button_//p
diff --git a/app/tests/mouse-keys/mkp/keycode_events.snapshot b/app/tests/mouse-keys/mkp/keycode_events.snapshot
new file mode 100644
index 0000000000..ab58cc9575
--- /dev/null
+++ b/app/tests/mouse-keys/mkp/keycode_events.snapshot
@@ -0,0 +1,10 @@
+press: Button 0 count 1
+press: Mouse buttons set to 0x01
+press: Button 1 count 1
+press: Mouse buttons set to 0x03
+release: Button 1 count: 0
+release: Button 1 released
+release: Mouse buttons set to 0x01
+release: Button 0 count: 0
+release: Button 0 released
+release: Mouse buttons set to 0x00
diff --git a/app/tests/mouse-keys/mmv/native_posix.keymap b/app/tests/mouse-keys/mkp/native_posix.keymap
similarity index 64%
rename from app/tests/mouse-keys/mmv/native_posix.keymap
rename to app/tests/mouse-keys/mkp/native_posix.keymap
index ecf06601c0..dd55b1640f 100644
--- a/app/tests/mouse-keys/mmv/native_posix.keymap
+++ b/app/tests/mouse-keys/mkp/native_posix.keymap
@@ -10,8 +10,8 @@
default_layer {
bindings = <
- &mmv MOVE_LEFT &none
- &none &none
+ &mkp LCLK &none
+ &none &mkp RCLK
>;
};
};
@@ -20,7 +20,9 @@
&kscan {
events = <
- ZMK_MOCK_PRESS(0,0,100)
- ZMK_MOCK_RELEASE(0,0,10)
+ ZMK_MOCK_PRESS (0,0,100)
+ ZMK_MOCK_PRESS (1,1,100)
+ ZMK_MOCK_RELEASE(1,1, 10)
+ ZMK_MOCK_RELEASE(0,0, 10)
>;
-};
\ No newline at end of file
+};
diff --git a/app/tests/mouse-keys/mkp/native_posix_64.keymap b/app/tests/mouse-keys/mkp/native_posix_64.keymap
new file mode 100644
index 0000000000..dd55b1640f
--- /dev/null
+++ b/app/tests/mouse-keys/mkp/native_posix_64.keymap
@@ -0,0 +1,28 @@
+#include <behaviors.dtsi>
+#include <dt-bindings/zmk/keys.h>
+#include <dt-bindings/zmk/kscan_mock.h>
+#include <dt-bindings/zmk/mouse.h>
+
+/ {
+ keymap {
+ compatible = "zmk,keymap";
+ label ="Default keymap";
+
+ default_layer {
+ bindings = <
+ &mkp LCLK &none
+ &none &mkp RCLK
+ >;
+ };
+ };
+};
+
+
+&kscan {
+ events = <
+ ZMK_MOCK_PRESS (0,0,100)
+ ZMK_MOCK_PRESS (1,1,100)
+ ZMK_MOCK_RELEASE(1,1, 10)
+ ZMK_MOCK_RELEASE(0,0, 10)
+ >;
+};
diff --git a/app/tests/mouse-keys/mmv/events.patterns b/app/tests/mouse-keys/mmv/events.patterns
deleted file mode 100644
index 833100f6ac..0000000000
--- a/app/tests/mouse-keys/mmv/events.patterns
+++ /dev/null
@@ -1 +0,0 @@
-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
deleted file mode 100644
index 259501ba3d..0000000000
--- a/app/tests/mouse-keys/mmv/keycode_events.snapshot
+++ /dev/null
@@ -1,2 +0,0 @@
-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/docs/docs/behaviors/mouse-emulation.md b/docs/docs/behaviors/mouse-emulation.md
index efe095e7a0..299fad4b25 100644
--- a/docs/docs/behaviors/mouse-emulation.md
+++ b/docs/docs/behaviors/mouse-emulation.md
@@ -17,16 +17,6 @@ This feature should be enabled via a config option:
CONFIG_ZMK_MOUSE=y
```
-This option enables several others.
-
-### Dedicated thread processing
-
-`CONFIG_ZMK_MOUSE_WORK_QUEUE_DEDICATED` is enabled by default and separates the processing of mouse signals into a dedicated thread, significantly improving performance.
-
-### Tick rate configuration
-
-`CONFIG_ZMK_MOUSE_TICK_DURATION` sets the tick rate for mouse polling. It is set to 8 ms. by default.
-
## Keycode Defines
To make it easier to encode the HID keycode numeric values, most keymaps include
@@ -37,7 +27,7 @@ provided by ZMK near the top:
#include <dt-bindings/zmk/mouse.h>
```
-Doing so allows using a set of defines such as `MOVE_UP`, `MOVE_DOWN`, `LCLK` and `SCROLL_UP` with these behaviors.
+Doing so allows using a set of defines such as `LCLK` and `RCLK` with these behaviors.
## Mouse Button Press
@@ -53,58 +43,3 @@ Example:
```
&mkp LCLK
```
-
-## Mouse Movement
-
-This behavior is used to manipulate the cursor.
-
-### Behavior Binding
-
-- Reference: `&mmv`
-- Parameter: A `uint32` with the first 16 bits relating to horizontal movement
- and the last 16 - to vertical movement.
-
-Example:
-
-```
-&mmv MOVE_UP
-```
-
-## Mouse Scrolling
-
-This behaviour is used to scroll, both horizontally and vertically.
-
-### Behavior Binding
-
-- Reference: `&mwh`
-- Parameter: A `uint16` with the first 8 bits relating to horizontal movement
- and the last 8 - to vertical movement.
-
-Example:
-
-```
-&mwh SCROLL_UP
-```
-
-## Acceleration
-
-Both mouse movement and scrolling have independently configurable acceleration profiles with three parameters: delay before movement, time to max speed and the acceleration exponent.
-The exponent is usually set to 0 for constant speed, 1 for uniform acceleration or 2 for uniform jerk.
-
-These profiles can be configured inside your keymap:
-
-```
-&mmv {
- time-to-max-speed-ms = <500>;
-};
-
-&mwh {
- acceleration-exponent=<1>;
-};
-
-/ {
- keymap {
- ...
- };
-};
-```