Rebase mouse-ftc on main after Zephyr 3.2 update
This commit is contained in:
parent
11996ff7f0
commit
1fa827d813
41 changed files with 1321 additions and 9 deletions
|
@ -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)
|
||||
|
@ -54,6 +61,9 @@ if ((NOT CONFIG_ZMK_SPLIT) OR CONFIG_ZMK_SPLIT_ROLE_CENTRAL)
|
|||
target_sources_ifdef(CONFIG_ZMK_BEHAVIOR_SENSOR_ROTATE app PRIVATE src/behaviors/behavior_sensor_rotate.c)
|
||||
target_sources_ifdef(CONFIG_ZMK_BEHAVIOR_SENSOR_ROTATE_VAR app PRIVATE src/behaviors/behavior_sensor_rotate_var.c)
|
||||
target_sources_ifdef(CONFIG_ZMK_BEHAVIOR_SENSOR_ROTATE_COMMON app PRIVATE src/behaviors/behavior_sensor_rotate_common.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/behaviors/behavior_tap_dance.c)
|
||||
target_sources(app PRIVATE src/behavior_queue.c)
|
||||
|
|
11
app/Kconfig
11
app/Kconfig
|
@ -149,6 +149,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
|
||||
|
@ -308,6 +312,13 @@ endif
|
|||
#Display/LED Options
|
||||
endmenu
|
||||
|
||||
menu "Mouse Options"
|
||||
|
||||
rsource "src/mouse/Kconfig"
|
||||
|
||||
#Mouse Options
|
||||
endmenu
|
||||
|
||||
menu "Power Management"
|
||||
|
||||
config ZMK_BATTERY_REPORTING
|
||||
|
|
|
@ -18,4 +18,7 @@
|
|||
#include <behaviors/caps_word.dtsi>
|
||||
#include <behaviors/key_repeat.dtsi>
|
||||
#include <behaviors/backlight.dtsi>
|
||||
#include <behaviors/macros.dtsi>
|
||||
#include <behaviors/macros.dtsi>
|
||||
#include <behaviors/mouse_key_press.dtsi>
|
||||
#include <behaviors/mouse_move.dtsi>
|
||||
#include <behaviors/mouse_scroll.dtsi>
|
||||
|
|
9
app/dts/behaviors/mouse_key_press.dtsi
Normal file
9
app/dts/behaviors/mouse_key_press.dtsi
Normal file
|
@ -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>;
|
||||
};
|
||||
};
|
||||
};
|
12
app/dts/behaviors/mouse_move.dtsi
Normal file
12
app/dts/behaviors/mouse_move.dtsi
Normal file
|
@ -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>;
|
||||
};
|
||||
};
|
||||
};
|
12
app/dts/behaviors/mouse_scroll.dtsi
Normal file
12
app/dts/behaviors/mouse_scroll.dtsi
Normal file
|
@ -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>;
|
||||
};
|
||||
};
|
||||
};
|
|
@ -0,0 +1,5 @@
|
|||
description: Mouse key press/release behavior
|
||||
|
||||
compatible: "zmk,behavior-mouse-key-press"
|
||||
|
||||
include: one_param.yaml
|
13
app/dts/bindings/behaviors/zmk,behavior-mouse-move.yaml
Normal file
13
app/dts/bindings/behaviors/zmk,behavior-mouse-move.yaml
Normal file
|
@ -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
|
13
app/dts/bindings/behaviors/zmk,behavior-mouse-scroll.yaml
Normal file
13
app/dts/bindings/behaviors/zmk,behavior-mouse-scroll.yaml
Normal file
|
@ -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
|
|
@ -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
|
||||
|
|
55
app/include/dt-bindings/zmk/mouse.h
Normal file
55
app/include/dt-bindings/zmk/mouse.h
Normal file
|
@ -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)
|
|
@ -69,3 +69,4 @@ int zmk_endpoints_toggle_transport(void);
|
|||
struct zmk_endpoint_instance zmk_endpoints_selected(void);
|
||||
|
||||
int zmk_endpoints_send_report(uint16_t usage_page);
|
||||
int zmk_endpoints_send_mouse_report();
|
||||
|
|
27
app/include/zmk/events/mouse_button_state_changed.h
Normal file
27
app/include/zmk/events/mouse_button_state_changed.h
Normal file
|
@ -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 = ZMK_HID_USAGE_ID(encoded), .state = pressed, .timestamp = timestamp});
|
||||
}
|
33
app/include/zmk/events/mouse_move_state_changed.h
Normal file
33
app/include/zmk/events/mouse_move_state_changed.h
Normal file
|
@ -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});
|
||||
}
|
34
app/include/zmk/events/mouse_scroll_state_changed.h
Normal file
34
app/include/zmk/events/mouse_scroll_state_changed.h
Normal file
|
@ -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});
|
||||
}
|
39
app/include/zmk/events/mouse_tick.h
Normal file
39
app/include/zmk/events/mouse_tick.h
Normal file
|
@ -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(),
|
||||
});
|
||||
}
|
|
@ -10,6 +10,7 @@
|
|||
#include <zephyr/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>
|
||||
|
||||
|
@ -85,9 +86,85 @@ static const uint8_t zmk_hid_report_desc[] = {
|
|||
#else
|
||||
#error "A proper consumer HID report usage range must be selected"
|
||||
#endif
|
||||
/* REPORT_COUNT (CONFIG_ZMK_HID_CONSUMER_REPORT_SIZE) */
|
||||
HID_REPORT_COUNT(CONFIG_ZMK_HID_CONSUMER_REPORT_SIZE),
|
||||
/* INPUT (Data,Ary,Abs) */
|
||||
HID_INPUT(0x00),
|
||||
/* END COLLECTION */
|
||||
HID_END_COLLECTION,
|
||||
|
||||
/* USAGE_PAGE (Generic Desktop) */
|
||||
HID_USAGE_PAGE(HID_USAGE_GD),
|
||||
/* USAGE (Mouse) */
|
||||
HID_USAGE(HID_USAGE_GD_MOUSE),
|
||||
/* COLLECTION (Application) */
|
||||
HID_COLLECTION(HID_COLLECTION_APPLICATION),
|
||||
/* REPORT ID (4) */
|
||||
HID_REPORT_ID(0x04),
|
||||
/* USAGE (Pointer) */
|
||||
HID_USAGE(HID_USAGE_GD_POINTER),
|
||||
/* COLLECTION (Physical) */
|
||||
HID_COLLECTION(HID_COLLECTION_PHYSICAL),
|
||||
/* USAGE_PAGE (Button) */
|
||||
HID_USAGE_PAGE(HID_USAGE_BUTTON),
|
||||
/* USAGE_MINIMUM (0x1) (button 1?) */
|
||||
HID_USAGE_MIN8(0x01),
|
||||
/* USAGE_MAXIMUM (0x10) (button 5? Buttons up to 8 still work) */
|
||||
HID_USAGE_MAX8(0x10),
|
||||
/* LOGICAL_MINIMUM (0) */
|
||||
HID_LOGICAL_MIN8(0x00),
|
||||
/* LOGICAL_MAXIMUM (1) */
|
||||
HID_LOGICAL_MAX8(0x01),
|
||||
/* REPORT_SIZE (1) */
|
||||
HID_REPORT_SIZE(0x01),
|
||||
/* REPORT_COUNT (16) */
|
||||
HID_REPORT_COUNT(0x10),
|
||||
/* INPUT (Data,Var,Abs) */
|
||||
HID_INPUT(0x02),
|
||||
/* USAGE_PAGE (Generic Desktop) */
|
||||
HID_USAGE_PAGE(HID_USAGE_GD),
|
||||
/* LOGICAL_MINIMUM (-32767) */
|
||||
HID_LOGICAL_MIN16(0x01, 0x80),
|
||||
/* LOGICAL_MAXIMUM (32767) */
|
||||
HID_LOGICAL_MAX16(0xFF, 0x7F),
|
||||
/* REPORT_SIZE (16) */
|
||||
HID_REPORT_SIZE(0x10),
|
||||
/* REPORT_COUNT (2) */
|
||||
HID_REPORT_COUNT(0x02),
|
||||
/* USAGE (X) */ // Vertical scroll
|
||||
HID_USAGE(HID_USAGE_GD_X),
|
||||
/* USAGE (Y) */
|
||||
HID_USAGE(HID_USAGE_GD_Y),
|
||||
/* Input (Data,Var,Rel) */
|
||||
HID_INPUT(0x06),
|
||||
/* LOGICAL_MINIMUM (-127) */
|
||||
HID_LOGICAL_MIN8(0x81),
|
||||
/* LOGICAL_MAXIMUM (127) */
|
||||
HID_LOGICAL_MAX8(0x7F),
|
||||
/* REPORT_SIZE (8) */
|
||||
HID_REPORT_SIZE(0x08),
|
||||
/* REPORT_COUNT (1) */
|
||||
HID_REPORT_COUNT(0x01),
|
||||
/* USAGE (Wheel) */
|
||||
HID_USAGE(HID_USAGE_GD_WHEEL),
|
||||
/* Input (Data,Var,Rel) */
|
||||
HID_INPUT(0x06),
|
||||
/* USAGE_PAGE (Consumer) */ // Horizontal scroll
|
||||
HID_USAGE_PAGE(HID_USAGE_CONSUMER),
|
||||
/* USAGE (AC Pan) */
|
||||
0x0A,
|
||||
0x38,
|
||||
0x02,
|
||||
/* LOGICAL_MINIMUM (-127) */
|
||||
HID_LOGICAL_MIN8(0x81),
|
||||
/* LOGICAL_MAXIMUM (127) */
|
||||
HID_LOGICAL_MAX8(0x7F),
|
||||
/* REPORT_COUNT (1) */
|
||||
HID_REPORT_COUNT(0x01),
|
||||
/* Input (Data,Var,Rel) */
|
||||
HID_INPUT(0x06),
|
||||
/* END COLLECTION */
|
||||
HID_END_COLLECTION,
|
||||
/* END COLLECTION */
|
||||
HID_END_COLLECTION,
|
||||
};
|
||||
|
||||
|
@ -126,6 +203,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);
|
||||
|
@ -152,5 +242,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();
|
||||
|
|
|
@ -13,3 +13,5 @@ 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);
|
||||
int zmk_hog_send_mouse_report_direct(struct zmk_hid_mouse_report_body *body);
|
||||
|
|
30
app/include/zmk/mouse.h
Normal file
30
app/include/zmk/mouse.h
Normal file
|
@ -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();
|
48
app/src/behaviors/behavior_mouse_key_press.c
Normal file
48
app/src/behaviors/behavior_mouse_key_press.c
Normal file
|
@ -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, NULL, 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) */
|
57
app/src/behaviors/behavior_mouse_move.c
Normal file
57
app/src/behaviors/behavior_mouse_move.c
Normal file
|
@ -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, NULL, 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) */
|
58
app/src/behaviors/behavior_mouse_scroll.c
Normal file
58
app/src/behaviors/behavior_mouse_scroll.c
Normal file
|
@ -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, NULL, 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) */
|
|
@ -193,6 +193,40 @@ int zmk_endpoints_send_report(uint16_t usage_page) {
|
|||
return -ENOTSUP;
|
||||
}
|
||||
|
||||
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,
|
||||
|
@ -297,6 +331,7 @@ static int zmk_endpoints_init(const struct device *_arg) {
|
|||
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);
|
||||
|
|
10
app/src/events/mouse_button_state_changed.c
Normal file
10
app/src/events/mouse_button_state_changed.c
Normal file
|
@ -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);
|
10
app/src/events/mouse_move_state_changed.c
Normal file
10
app/src/events/mouse_move_state_changed.c
Normal file
|
@ -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);
|
10
app/src/events/mouse_scroll_state_changed.c
Normal file
10
app/src/events/mouse_scroll_state_changed.c
Normal file
|
@ -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);
|
10
app/src/events/mouse_tick.c
Normal file
10
app/src/events/mouse_tick.c
Normal file
|
@ -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);
|
|
@ -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};
|
||||
|
@ -264,6 +267,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;
|
||||
}
|
||||
|
@ -271,3 +353,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;
|
||||
}
|
||||
|
|
|
@ -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>
|
||||
|
@ -86,13 +85,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;
|
||||
}
|
||||
|
|
|
@ -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, ¬ify_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, ¬ify_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),
|
||||
|
|
|
@ -17,6 +17,12 @@ 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) {
|
||||
LOG_INF("Welcome to ZMK!\n");
|
||||
|
||||
|
@ -27,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 */
|
||||
}
|
||||
|
|
38
app/src/mouse/Kconfig
Normal file
38
app/src/mouse/Kconfig
Normal file
|
@ -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
|
163
app/src/mouse/key_listener.c
Normal file
163
app/src/mouse/key_listener.c
Normal file
|
@ -0,0 +1,163 @@
|
|||
/*
|
||||
* 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>
|
||||
|
||||
#if !defined(CONFIG_ZMK_SPLIT) || defined(CONFIG_ZMK_SPLIT_ROLE_CENTRAL)
|
||||
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);
|
||||
|
||||
#endif /*!defined(CONFIG_ZMK_SPLIT) || defined(CONFIG_ZMK_SPLIT_ROLE_CENTRAL) */
|
31
app/src/mouse/main.c
Normal file
31
app/src/mouse/main.c
Normal file
|
@ -0,0 +1,31 @@
|
|||
/*
|
||||
* 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_queue_start(&mouse_work_q, mouse_work_stack_area,
|
||||
K_THREAD_STACK_SIZEOF(mouse_work_stack_area),
|
||||
CONFIG_ZMK_MOUSE_DEDICATED_THREAD_PRIORITY,
|
||||
NULL);
|
||||
#endif
|
||||
return 0;
|
||||
}
|
106
app/src/mouse/tick_listener.c
Normal file
106
app/src/mouse/tick_listener.c
Normal file
|
@ -0,0 +1,106 @@
|
|||
/*
|
||||
* 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/hid.h>
|
||||
#include <zmk/mouse.h>
|
||||
|
||||
#include <sys/util.h> // CLAMP
|
||||
|
||||
#if !defined(CONFIG_ZMK_SPLIT) || defined(CONFIG_ZMK_SPLIT_ROLE_CENTRAL)
|
||||
#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);
|
||||
|
||||
#endif /* !defined(CONFIG_ZMK_SPLIT) || defined(CONFIG_ZMK_SPLIT_ROLE_CENTRAL) */
|
1
app/tests/mouse-keys/mmv/events.patterns
Normal file
1
app/tests/mouse-keys/mmv/events.patterns
Normal file
|
@ -0,0 +1 @@
|
|||
s/.*hid_listener_keycode_//p
|
2
app/tests/mouse-keys/mmv/keycode_events.snapshot
Normal file
2
app/tests/mouse-keys/mmv/keycode_events.snapshot
Normal file
|
@ -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
|
26
app/tests/mouse-keys/mmv/native_posix.keymap
Normal file
26
app/tests/mouse-keys/mmv/native_posix.keymap
Normal file
|
@ -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)
|
||||
>;
|
||||
};
|
110
docs/docs/behaviors/mouse-emulation.md
Normal file
110
docs/docs/behaviors/mouse-emulation.md
Normal file
|
@ -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 {
|
||||
...
|
||||
};
|
||||
};
|
||||
```
|
|
@ -33,7 +33,7 @@ ZMK is currently missing some features found in other popular firmware. This tab
|
|||
| One Shot Keys | ✅ | ✅ | ✅ |
|
||||
| [Combo Keys](features/combos.md) | ✅ | | ✅ |
|
||||
| [Macros](behaviors/macros.md) | ✅ | ✅ | ✅ |
|
||||
| Mouse Keys | 🚧 | ✅ | ✅ |
|
||||
| Mouse Keys | ✅ | ✅ | ✅ |
|
||||
| Low Active Power Usage | ✅ | | |
|
||||
| Low Power Sleep States | ✅ | ✅ | |
|
||||
| [Low Power Mode (VCC Shutoff)](behaviors/power.md) | ✅ | ✅ | |
|
||||
|
|
|
@ -36,6 +36,7 @@ module.exports = {
|
|||
"behaviors/caps-word",
|
||||
"behaviors/key-repeat",
|
||||
"behaviors/sensor-rotate",
|
||||
"behaviors/mouse-emulation",
|
||||
"behaviors/reset",
|
||||
"behaviors/bluetooth",
|
||||
"behaviors/outputs",
|
||||
|
|
Loading…
Add table
Reference in a new issue