Shawn Meier
3 years ago
committed by
Julia Luna
41 changed files with 1351 additions and 14 deletions
@ -0,0 +1,9 @@
@@ -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>; |
||||
}; |
||||
}; |
||||
}; |
@ -0,0 +1,12 @@
@@ -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>; |
||||
}; |
||||
}; |
||||
}; |
@ -0,0 +1,12 @@
@@ -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 @@
@@ -0,0 +1,5 @@
|
||||
description: Mouse key press/release behavior |
||||
|
||||
compatible: "zmk,behavior-mouse-key-press" |
||||
|
||||
include: one_param.yaml |
@ -0,0 +1,13 @@
@@ -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 |
@ -0,0 +1,13 @@
@@ -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 |
@ -0,0 +1,55 @@
@@ -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) |
@ -0,0 +1,27 @@
@@ -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}); |
||||
} |
@ -0,0 +1,33 @@
@@ -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}); |
||||
} |
@ -0,0 +1,34 @@
@@ -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}); |
||||
} |
@ -0,0 +1,39 @@
@@ -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(), |
||||
}); |
||||
} |
@ -0,0 +1,30 @@
@@ -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(); |
@ -0,0 +1,48 @@
@@ -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) */ |
@ -0,0 +1,57 @@
@@ -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) */ |
@ -0,0 +1,58 @@
@@ -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) */ |
@ -0,0 +1,10 @@
@@ -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); |
@ -0,0 +1,10 @@
@@ -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); |
@ -0,0 +1,10 @@
@@ -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); |
@ -0,0 +1,10 @@
@@ -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); |
@ -0,0 +1,38 @@
@@ -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 |
@ -0,0 +1,160 @@
@@ -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); |
@ -0,0 +1,30 @@
@@ -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; |
||||
} |
@ -0,0 +1,102 @@
@@ -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); |
@ -0,0 +1 @@
@@ -0,0 +1 @@
|
||||
s/.*hid_listener_keycode_//p |
@ -0,0 +1,2 @@
@@ -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 |
@ -0,0 +1,26 @@
@@ -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) |
||||
>; |
||||
}; |
@ -0,0 +1,110 @@
@@ -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 { |
||||
... |
||||
}; |
||||
}; |
||||
``` |
Loading…
Reference in new issue