Compare commits

..

No commits in common. "feat/pointers-move-scroll" and "main" have entirely different histories.

49 changed files with 106 additions and 1181 deletions

View file

@ -36,6 +36,7 @@ target_sources_ifdef(CONFIG_ZMK_GPIO_KEY_WAKEUP_TRIGGER app PRIVATE src/gpio_key
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_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)
@ -43,7 +44,7 @@ target_sources_ifdef(CONFIG_ZMK_EXT_POWER app PRIVATE src/behaviors/behavior_ext
target_sources_ifdef(CONFIG_ZMK_BEHAVIOR_SOFT_OFF app PRIVATE src/behaviors/behavior_soft_off.c)
if ((NOT CONFIG_ZMK_SPLIT) OR CONFIG_ZMK_SPLIT_ROLE_CENTRAL)
target_sources(app PRIVATE src/hid.c)
target_sources_ifdef(CONFIG_ZMK_MOUSE app PRIVATE src/mouse/input_listener.c)
target_sources_ifdef(CONFIG_ZMK_MOUSE 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_ifdef(CONFIG_ZMK_BEHAVIOR_HOLD_TAP app PRIVATE src/behaviors/behavior_hold_tap.c)
@ -63,7 +64,6 @@ if ((NOT CONFIG_ZMK_SPLIT) OR CONFIG_ZMK_SPLIT_ROLE_CENTRAL)
target_sources_ifdef(CONFIG_ZMK_BEHAVIOR_SENSOR_ROTATE_COMMON app PRIVATE src/behaviors/behavior_sensor_rotate_common.c)
target_sources_ifdef(CONFIG_ZMK_BEHAVIOR_MOUSE_KEY_PRESS app PRIVATE src/behaviors/behavior_mouse_key_press.c)
target_sources_ifdef(CONFIG_ZMK_BEHAVIOR_STUDIO_UNLOCK app PRIVATE src/behaviors/behavior_studio_unlock.c)
target_sources_ifdef(CONFIG_ZMK_BEHAVIOR_INPUT_TWO_AXIS app PRIVATE src/behaviors/behavior_input_two_axis.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)

View file

@ -395,7 +395,13 @@ endif
#Display/LED Options
endmenu
rsource "src/mouse/Kconfig"
menu "Mouse Options"
config ZMK_MOUSE
bool "Enable ZMK mouse emulation"
#Mouse Options
endmenu
menu "Power Management"

View file

@ -71,7 +71,8 @@ config ZMK_BEHAVIOR_KEY_TOGGLE
config ZMK_BEHAVIOR_MOUSE_KEY_PRESS
bool
default y
depends on DT_HAS_ZMK_BEHAVIOR_MOUSE_KEY_PRESS_ENABLED && ZMK_MOUSE
depends on DT_HAS_ZMK_BEHAVIOR_MOUSE_KEY_PRESS_ENABLED
imply ZMK_MOUSE
config ZMK_BEHAVIOR_STICKY_KEY
bool
@ -93,11 +94,6 @@ config ZMK_BEHAVIOR_SOFT_OFF
default y
depends on DT_HAS_ZMK_BEHAVIOR_SOFT_OFF_ENABLED && ZMK_PM_SOFT_OFF
config ZMK_BEHAVIOR_INPUT_TWO_AXIS
bool
default y
depends on DT_HAS_ZMK_BEHAVIOR_INPUT_TWO_AXIS_ENABLED && ZMK_MOUSE
config ZMK_BEHAVIOR_SENSOR_ROTATE_COMMON
bool

View file

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

View file

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

View file

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

View file

@ -1,25 +0,0 @@
/*
* Copyright (c) 2024 The ZMK Contributors
*
* SPDX-License-Identifier: MIT
*/
#include <zephyr/dt-bindings/input/input-event-codes.h>
/ {
behaviors {
/omit-if-no-ref/ mmv: mouse_move {
compatible = "zmk,behavior-input-two-axis";
#binding-cells = <1>;
x-input-code = <INPUT_REL_X>;
y-input-code = <INPUT_REL_Y>;
time-to-max-speed-ms = <300>;
acceleration-exponent = <1>;
};
};
mmv_input_listener: mmv_input_listener {
compatible = "zmk,input-listener";
device = <&mmv>;
};
};

View file

@ -1,26 +0,0 @@
/*
* Copyright (c) 2024 The ZMK Contributors
*
* SPDX-License-Identifier: MIT
*/
#include <zephyr/dt-bindings/input/input-event-codes.h>
/ {
behaviors {
/omit-if-no-ref/ msc: mouse_scroll {
compatible = "zmk,behavior-input-two-axis";
#binding-cells = <1>;
x-input-code = <INPUT_REL_HWHEEL>;
y-input-code = <INPUT_REL_WHEEL>;
time-to-max-speed-ms = <300>;
acceleration-exponent = <0>;
};
};
msc_input_listener: msc_input_listener {
compatible = "zmk,input-listener";
device = <&msc>;
};
};

View file

@ -1,25 +0,0 @@
description: Two axis input behavior
compatible: "zmk,behavior-input-two-axis"
include: one_param.yaml
properties:
x-input-code:
type: int
required: true
y-input-code:
type: int
required: true
trigger-period-ms:
type: int
default: 16
description: The time (in ms) between generated inputs when an input has non-zero speed.
delay-ms:
type: int
time-to-max-speed-ms:
type: int
required: true
acceleration-exponent:
type: int
default: 1

View file

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

View file

@ -1,5 +1,5 @@
/*
* Copyright (c) 2023 The ZMK Contributors
* Copyright (c) 2020 The ZMK Contributors
*
* SPDX-License-Identifier: MIT
*/
@ -22,29 +22,3 @@
#define MB4 BIT(3)
#define MB5 BIT(4)
#ifndef ZMK_MOUSE_DEFAULT_MOVE_VAL
#define ZMK_MOUSE_DEFAULT_MOVE_VAL 600
#endif
#ifndef ZMK_MOUSE_DEFAULT_SCRL_VAL
#define ZMK_MOUSE_DEFAULT_SCRL_VAL 10
#endif
/* Mouse move behavior */
#define MOVE_Y(vert) ((vert)&0xFFFF)
#define MOVE_Y_DECODE(encoded) (int16_t)((encoded)&0x0000FFFF)
#define MOVE_X(hor) (((hor)&0xFFFF) << 16)
#define MOVE_X_DECODE(encoded) (int16_t)(((encoded)&0xFFFF0000) >> 16)
#define MOVE(hor, vert) (MOVE_X(hor) + MOVE_Y(vert))
#define MOVE_UP MOVE_Y(-ZMK_MOUSE_DEFAULT_MOVE_VAL)
#define MOVE_DOWN MOVE_Y(ZMK_MOUSE_DEFAULT_MOVE_VAL)
#define MOVE_LEFT MOVE_X(-ZMK_MOUSE_DEFAULT_MOVE_VAL)
#define MOVE_RIGHT MOVE_X(ZMK_MOUSE_DEFAULT_MOVE_VAL)
#define SCRL_UP MOVE_Y(ZMK_MOUSE_DEFAULT_SCRL_VAL)
#define SCRL_DOWN MOVE_Y(-ZMK_MOUSE_DEFAULT_SCRL_VAL)
#define SCRL_LEFT MOVE_X(-ZMK_MOUSE_DEFAULT_SCRL_VAL)
#define SCRL_RIGHT MOVE_X(ZMK_MOUSE_DEFAULT_SCRL_VAL)

View file

@ -77,10 +77,6 @@
#define ZMK_HID_REPORT_ID_CONSUMER 0x02
#define ZMK_HID_REPORT_ID_MOUSE 0x03
#define HID_USAGE16(a, b) HID_ITEM(HID_ITEM_TAG_USAGE, HID_ITEM_TYPE_LOCAL, 2), a, b
#define HID_USAGE16_SINGLE(a) HID_USAGE16((a & 0xFF), ((a >> 8) & 0xFF))
static const uint8_t zmk_hid_report_desc[] = {
HID_USAGE_PAGE(HID_USAGE_GEN_DESKTOP),
HID_USAGE(HID_USAGE_GD_KEYBOARD),
@ -189,17 +185,10 @@ static const uint8_t zmk_hid_report_desc[] = {
HID_USAGE(HID_USAGE_GD_X),
HID_USAGE(HID_USAGE_GD_Y),
HID_USAGE(HID_USAGE_GD_WHEEL),
HID_LOGICAL_MIN16(0xFF, -0x7F),
HID_LOGICAL_MAX16(0xFF, 0x7F),
HID_REPORT_SIZE(0x10),
HID_REPORT_COUNT(0x03),
HID_INPUT(ZMK_HID_MAIN_VAL_DATA | ZMK_HID_MAIN_VAL_VAR | ZMK_HID_MAIN_VAL_REL),
HID_USAGE_PAGE(HID_USAGE_CONSUMER),
HID_USAGE16_SINGLE(HID_USAGE_CONSUMER_AC_PAN),
HID_LOGICAL_MIN16(0xFF, -0x7F),
HID_LOGICAL_MAX16(0xFF, 0x7F),
HID_LOGICAL_MIN8(-0x7F),
HID_LOGICAL_MAX8(0x7F),
HID_REPORT_SIZE(0x08),
HID_REPORT_COUNT(0x01),
HID_REPORT_COUNT(0x03),
HID_INPUT(ZMK_HID_MAIN_VAL_DATA | ZMK_HID_MAIN_VAL_VAR | ZMK_HID_MAIN_VAL_REL),
HID_END_COLLECTION,
HID_END_COLLECTION,
@ -269,10 +258,9 @@ struct zmk_hid_consumer_report {
#if IS_ENABLED(CONFIG_ZMK_MOUSE)
struct zmk_hid_mouse_report_body {
zmk_mouse_button_flags_t buttons;
int16_t d_x;
int16_t d_y;
int16_t d_scroll_y;
int16_t d_scroll_x;
int8_t d_x;
int8_t d_y;
int8_t d_wheel;
} __packed;
struct zmk_hid_mouse_report {
@ -313,10 +301,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(void);
#endif // IS_ENABLED(CONFIG_ZMK_MOUSE)

View file

@ -1,5 +1,5 @@
/*
* Copyright (c) 2023 The ZMK Contributors
* Copyright (c) 2021 The ZMK Contributors
*
* SPDX-License-Identifier: MIT
*/

View file

@ -1,265 +0,0 @@
/*
* Copyright (c) 2021 The ZMK Contributors
*
* SPDX-License-Identifier: MIT
*/
#define DT_DRV_COMPAT zmk_behavior_input_two_axis
#include <zephyr/device.h>
#include <drivers/behavior.h>
#include <zephyr/input/input.h>
#include <zephyr/logging/log.h>
#include <zephyr/sys/util.h> // CLAMP
#include <zmk/behavior.h>
#include <dt-bindings/zmk/mouse.h>
LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL);
struct vector2d {
float x;
float y;
};
struct movement_state_1d {
float remainder;
int16_t speed;
uint64_t start_time;
};
struct movement_state_2d {
struct movement_state_1d x;
struct movement_state_1d y;
};
struct behavior_input_two_axis_data {
struct k_work_delayable tick_work;
const struct device *dev;
struct movement_state_2d state;
};
struct behavior_input_two_axis_config {
int16_t x_code;
int16_t y_code;
uint16_t delay_ms;
uint16_t time_to_max_speed_ms;
uint8_t trigger_period_ms;
// acceleration exponent 0: uniform speed
// acceleration exponent 1: uniform acceleration
// acceleration exponent 2: uniform jerk
uint8_t acceleration_exponent;
};
#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
static int64_t ms_since_start(int64_t start, int64_t now, int64_t delay) {
if (start == 0) {
return 0;
}
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 behavior_input_two_axis_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 == 0) {
return 0;
}
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 float update_movement_1d(const struct behavior_input_two_axis_config *config,
struct movement_state_1d *state, int64_t now) {
float move = 0;
if (state->speed == 0) {
state->remainder = 0;
return move;
}
int64_t move_duration = ms_since_start(state->start_time, now, config->delay_ms);
move = (move_duration > 0)
? (speed(config, state->speed, move_duration) * config->trigger_period_ms / 1000)
: 0;
track_remainder(&(move), &(state->remainder));
return move;
}
static struct vector2d update_movement_2d(const struct behavior_input_two_axis_config *config,
struct movement_state_2d *state, int64_t now) {
struct vector2d move = {0};
move = (struct vector2d){
.x = update_movement_1d(config, &state->x, now),
.y = update_movement_1d(config, &state->y, now),
};
return move;
}
static bool is_non_zero_1d_movement(int16_t speed) { return speed != 0; }
static bool is_non_zero_2d_movement(struct movement_state_2d *state) {
return is_non_zero_1d_movement(state->x.speed) || is_non_zero_1d_movement(state->y.speed);
}
static bool should_be_working(struct behavior_input_two_axis_data *data) {
return is_non_zero_2d_movement(&data->state);
}
static void tick_work_cb(struct k_work *work) {
struct k_work_delayable *d_work = k_work_delayable_from_work(work);
struct behavior_input_two_axis_data *data =
CONTAINER_OF(d_work, struct behavior_input_two_axis_data, tick_work);
const struct device *dev = data->dev;
const struct behavior_input_two_axis_config *cfg = dev->config;
uint64_t timestamp = k_uptime_get();
LOG_INF("x start: %llu, y start: %llu, current timestamp: %llu", data->state.x.start_time,
data->state.y.start_time, timestamp);
struct vector2d move = update_movement_2d(cfg, &data->state, timestamp);
int ret = 0;
bool have_x = is_non_zero_1d_movement(move.x);
bool have_y = is_non_zero_1d_movement(move.y);
if (have_x) {
ret = input_report_rel(dev, cfg->x_code, (int16_t)CLAMP(move.x, INT16_MIN, INT16_MAX),
!have_y, K_NO_WAIT);
}
if (have_y) {
ret = input_report_rel(dev, cfg->y_code, (int16_t)CLAMP(move.y, INT16_MIN, INT16_MAX), true,
K_NO_WAIT);
}
if (should_be_working(data)) {
k_work_schedule(&data->tick_work, K_MSEC(cfg->trigger_period_ms));
}
}
static void set_start_times_for_activity_1d(struct movement_state_1d *state) {
if (state->speed != 0 && state->start_time == 0) {
state->start_time = k_uptime_get();
} else if (state->speed == 0) {
state->start_time = 0;
}
}
static void set_start_times_for_activity(struct movement_state_2d *state) {
set_start_times_for_activity_1d(&state->x);
set_start_times_for_activity_1d(&state->y);
}
static void update_work_scheduling(const struct device *dev) {
struct behavior_input_two_axis_data *data = dev->data;
const struct behavior_input_two_axis_config *cfg = dev->config;
set_start_times_for_activity(&data->state);
if (should_be_working(data)) {
k_work_schedule(&data->tick_work, K_MSEC(cfg->trigger_period_ms));
} else {
k_work_cancel_delayable(&data->tick_work);
}
}
int behavior_input_two_axis_adjust_speed(const struct device *dev, int16_t dx, int16_t dy) {
struct behavior_input_two_axis_data *data = dev->data;
LOG_DBG("Adjusting: %d %d", dx, dy);
data->state.x.speed += dx;
data->state.y.speed += dy;
LOG_DBG("After: %d %d", data->state.x.speed, data->state.y.speed);
update_work_scheduling(dev);
return 0;
}
static int behavior_input_two_axis_init(const struct device *dev) {
struct behavior_input_two_axis_data *data = dev->data;
data->dev = dev;
k_work_init_delayable(&data->tick_work, tick_work_cb);
return 0;
};
static int on_keymap_binding_pressed(struct zmk_behavior_binding *binding,
struct zmk_behavior_binding_event event) {
const struct device *behavior_dev = zmk_behavior_get_binding(binding->behavior_dev);
LOG_DBG("position %d keycode 0x%02X", event.position, binding->param1);
int16_t x = MOVE_X_DECODE(binding->param1);
int16_t y = MOVE_Y_DECODE(binding->param1);
behavior_input_two_axis_adjust_speed(behavior_dev, x, y);
return 0;
}
static int on_keymap_binding_released(struct zmk_behavior_binding *binding,
struct zmk_behavior_binding_event event) {
const struct device *behavior_dev = zmk_behavior_get_binding(binding->behavior_dev);
LOG_DBG("position %d keycode 0x%02X", event.position, binding->param1);
int16_t x = MOVE_X_DECODE(binding->param1);
int16_t y = MOVE_Y_DECODE(binding->param1);
behavior_input_two_axis_adjust_speed(behavior_dev, -x, -y);
return 0;
}
static const struct behavior_driver_api behavior_input_two_axis_driver_api = {
.binding_pressed = on_keymap_binding_pressed, .binding_released = on_keymap_binding_released};
#define ITA_INST(n) \
static struct behavior_input_two_axis_data behavior_input_two_axis_data_##n = {}; \
static struct behavior_input_two_axis_config behavior_input_two_axis_config_##n = { \
.x_code = DT_INST_PROP(n, x_input_code), \
.y_code = DT_INST_PROP(n, y_input_code), \
.trigger_period_ms = DT_INST_PROP(n, trigger_period_ms), \
.delay_ms = DT_INST_PROP_OR(n, delay_ms, 0), \
.time_to_max_speed_ms = DT_INST_PROP(n, time_to_max_speed_ms), \
.acceleration_exponent = DT_INST_PROP_OR(n, acceleration_exponent, 1), \
}; \
BEHAVIOR_DT_INST_DEFINE( \
n, behavior_input_two_axis_init, NULL, &behavior_input_two_axis_data_##n, \
&behavior_input_two_axis_config_##n, POST_KERNEL, CONFIG_KERNEL_INIT_PRIORITY_DEFAULT, \
&behavior_input_two_axis_driver_api);
DT_INST_FOREACH_STATUS_OKAY(ITA_INST)

View file

@ -11,9 +11,8 @@
#include <zephyr/logging/log.h>
#include <zmk/behavior.h>
#include <zmk/hid.h>
#include <zephyr/input/input.h>
#include <zephyr/dt-bindings/input/input-event-codes.h>
#include <zmk/event_manager.h>
#include <zmk/events/mouse_button_state_changed.h>
LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL);
@ -21,31 +20,19 @@ LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL);
static int behavior_mouse_key_press_init(const struct device *dev) { return 0; };
static void process_key_state(const struct device *dev, int32_t val, bool pressed) {
for (int i = 0; i < ZMK_HID_MOUSE_NUM_BUTTONS; i++) {
if (val & BIT(i)) {
WRITE_BIT(val, i, 0);
input_report_key(dev, INPUT_BTN_0 + i, pressed ? 1 : 0, val == 0, K_FOREVER);
}
}
}
static int 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);
process_key_state(zmk_behavior_get_binding(binding->behavior_dev), binding->param1, true);
return 0;
return 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);
process_key_state(zmk_behavior_get_binding(binding->behavior_dev), binding->param1, false);
return 0;
return 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 = {

View file

@ -27,9 +27,8 @@ static uint8_t keys_held = 0;
#if IS_ENABLED(CONFIG_ZMK_MOUSE)
static struct zmk_hid_mouse_report mouse_report = {
.report_id = ZMK_HID_REPORT_ID_MOUSE,
.body = {.buttons = 0, .d_x = 0, .d_y = 0, .d_scroll_y = 0}};
static struct zmk_hid_mouse_report mouse_report = {.report_id = ZMK_HID_REPORT_ID_MOUSE,
.body = {.buttons = 0}};
#endif // IS_ENABLED(CONFIG_ZMK_MOUSE)
@ -432,37 +431,7 @@ 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.d_x = x;
mouse_report.body.d_y = y;
LOG_DBG("Mouse movement set to %d/%d", mouse_report.body.d_x, mouse_report.body.d_y);
}
void zmk_hid_mouse_movement_update(int16_t x, int16_t y) {
mouse_report.body.d_x += x;
mouse_report.body.d_y += y;
LOG_DBG("Mouse movement updated to %d/%d", mouse_report.body.d_x, mouse_report.body.d_y);
}
void zmk_hid_mouse_scroll_set(int8_t x, int8_t y) {
mouse_report.body.d_scroll_x = x;
mouse_report.body.d_scroll_y = y;
LOG_DBG("Mouse scroll set to %d/%d", mouse_report.body.d_scroll_x,
mouse_report.body.d_scroll_y);
}
void zmk_hid_mouse_scroll_update(int8_t x, int8_t y) {
mouse_report.body.d_scroll_x += x;
mouse_report.body.d_scroll_y += y;
LOG_DBG("Mouse scroll updated to X: %d/%d", mouse_report.body.d_scroll_x,
mouse_report.body.d_scroll_y);
}
void zmk_hid_mouse_clear(void) {
LOG_DBG("Mouse report cleared");
memset(&mouse_report.body, 0, sizeof(mouse_report.body));
}
void zmk_hid_mouse_clear(void) { memset(&mouse_report.body, 0, sizeof(mouse_report.body)); }
#endif // IS_ENABLED(CONFIG_ZMK_MOUSE)

View file

@ -380,6 +380,7 @@ int zmk_hog_send_mouse_report(struct zmk_hid_mouse_report_body *report) {
return 0;
};
#endif // IS_ENABLED(CONFIG_ZMK_MOUSE)
static int zmk_hog_init(void) {

View file

@ -14,10 +14,6 @@ LOG_MODULE_REGISTER(zmk, CONFIG_ZMK_LOG_LEVEL);
#include <zmk/display.h>
#ifdef CONFIG_ZMK_MOUSE
#include <zmk/mouse.h>
#endif /* CONFIG_ZMK_MOUSE */
int main(void) {
LOG_INF("Welcome to ZMK!\n");

43
app/src/mouse.c Normal file
View file

@ -0,0 +1,43 @@
/*
* Copyright (c) 2021 The ZMK Contributors
*
* SPDX-License-Identifier: MIT
*/
#include <drivers/behavior.h>
#include <zephyr/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);

View file

@ -1,8 +0,0 @@
# Copyright (c) 2023 The ZMK Contributors
# SPDX-License-Identifier: MIT
config ZMK_MOUSE
bool "Mouse Emulation"
select INPUT
select INPUT_THREAD_PRIORITY_OVERRIDE

View file

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

View file

@ -1,6 +0,0 @@
CONFIG_GPIO=n
CONFIG_ZMK_BLE=n
CONFIG_LOG=y
CONFIG_LOG_BACKEND_SHOW_COLOR=n
CONFIG_ZMK_LOG_LEVEL_DBG=y
CONFIG_ZMK_MOUSE=y

View file

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

View file

@ -1,6 +0,0 @@
CONFIG_GPIO=n
CONFIG_ZMK_BLE=n
CONFIG_LOG=y
CONFIG_LOG_BACKEND_SHOW_COLOR=n
CONFIG_ZMK_LOG_LEVEL_DBG=y
CONFIG_ZMK_MOUSE=y

View file

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

View file

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

View file

@ -1,6 +0,0 @@
CONFIG_GPIO=n
CONFIG_ZMK_BLE=n
CONFIG_LOG=y
CONFIG_LOG_BACKEND_SHOW_COLOR=n
CONFIG_ZMK_LOG_LEVEL_DBG=y
CONFIG_ZMK_MOUSE=y

View file

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

View file

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

View file

@ -1,6 +0,0 @@
CONFIG_GPIO=n
CONFIG_ZMK_BLE=n
CONFIG_LOG=y
CONFIG_LOG_BACKEND_SHOW_COLOR=n
CONFIG_ZMK_LOG_LEVEL_DBG=y
CONFIG_ZMK_MOUSE=y

View file

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

View file

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

View file

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

View file

@ -1,6 +0,0 @@
CONFIG_GPIO=n
CONFIG_ZMK_BLE=n
CONFIG_LOG=y
CONFIG_LOG_BACKEND_SHOW_COLOR=n
CONFIG_ZMK_LOG_LEVEL_DBG=y
CONFIG_ZMK_MOUSE=y

View file

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

View file

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

View file

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

View file

@ -1,6 +0,0 @@
CONFIG_GPIO=n
CONFIG_ZMK_BLE=n
CONFIG_LOG=y
CONFIG_LOG_BACKEND_SHOW_COLOR=n
CONFIG_ZMK_LOG_LEVEL_DBG=y
CONFIG_ZMK_MOUSE=y

View file

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

View file

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

View file

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

View file

@ -1,6 +0,0 @@
CONFIG_GPIO=n
CONFIG_ZMK_BLE=n
CONFIG_LOG=y
CONFIG_LOG_BACKEND_SHOW_COLOR=n
CONFIG_ZMK_LOG_LEVEL_DBG=y
CONFIG_ZMK_MOUSE=y

View file

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

View file

@ -14,34 +14,34 @@ ZMK is currently missing some features found in other popular firmware. This tab
| Legend: | ✅ Supported | 🚧 Under Development | 💡 Planned |
| :------ | :----------- | :------------------- | :--------- |
| **Feature** | ZMK | BlueMicro | QMK |
| ---------------------------------------------------------------------------------------------------------------------------------- | :-: | :-------: | :-: |
| Low Latency BLE Support | ✅ | ✅ | |
| Multi-Device BLE Support | ✅ | | |
| [USB Connectivity](behaviors/outputs.md) | ✅ | ✅ | ✅ |
| User Configuration Repositories | ✅ | | |
| Split Keyboard Support | ✅ | ✅ | ✅ |
| [Keymaps and Layers](behaviors/layers.md) | ✅ | ✅ | ✅ |
| [Hold-Tap](behaviors/hold-tap.mdx) (which includes [Mod-Tap](behaviors/mod-tap.md) and [Layer-Tap](behaviors/layers.md#layer-tap)) | ✅ | ✅ | ✅ |
| [Tap-Dance](behaviors/tap-dance.mdx) | ✅ | ✅[^2] | ✅ |
| [Keyboard Codes](codes/index.mdx#keyboard) | ✅ | ✅ | ✅ |
| [Media](codes/index.mdx#media-controls) & [Consumer](codes/index.mdx#consumer-controls) Codes | ✅ | ✅ | ✅ |
| [Encoders](features/encoders.md) | ✅ | ✅ | ✅ |
| [Display Support](features/displays.md)[^1] | 🚧 | 🚧 | ✅ |
| [RGB Underglow](features/underglow.md) | ✅ | ✅ | ✅ |
| [Backlight](features/backlight.mdx) | ✅ | ✅ | ✅ |
| One Shot Keys | ✅ | ✅ | ✅ |
| [Combo Keys](features/combos.md) | ✅ | | ✅ |
| [Macros](behaviors/macros.md) | ✅ | ✅ | ✅ |
| Mouse Keys | ✅ | ✅ | ✅ |
| Low Active Power Usage | ✅ | | |
| Low Power Sleep States | ✅ | ✅ | |
| [Low Power Mode (VCC Shutoff)](behaviors/power.md) | ✅ | ✅ | |
| Battery Reporting | ✅ | ✅ | |
| Shell over BLE | 💡 | | |
| Realtime Keymap Updating | 💡 | | ✅ |
| AVR/8 Bit | | | ✅ |
| [Wide Range of ARM Chips Supported](https://docs.zephyrproject.org/3.5.0/boards/index.html) | ✅ | | |
| **Feature** | ZMK | BlueMicro | QMK |
| ---------------------------------------------------------------------------------------------------------------------------------------------------------- | :-: | :-------: | :-: |
| Low Latency BLE Support | ✅ | ✅ | |
| Multi-Device BLE Support | ✅ | | |
| [USB Connectivity](keymaps/behaviors/outputs.md) | ✅ | ✅ | ✅ |
| User Configuration Repositories | ✅ | | |
| Split Keyboard Support | ✅ | ✅ | ✅ |
| [Keymaps and Layers](keymaps/behaviors/layers.md) | ✅ | ✅ | ✅ |
| [Hold-Tap](keymaps/behaviors/hold-tap.mdx) (which includes [Mod-Tap](keymaps/behaviors/mod-tap.md) and [Layer-Tap](keymaps/behaviors/layers.md#layer-tap)) | ✅ | ✅ | ✅ |
| [Tap-Dance](keymaps/behaviors/tap-dance.mdx) | ✅ | ✅[^2] | ✅ |
| [Keyboard Keycodes](keymaps/list-of-keycodes.mdx#keyboard) | ✅ | ✅ | ✅ |
| [Media](keymaps/list-of-keycodes.mdx#media-controls) & [Consumer](keymaps/list-of-keycodes.mdx#consumer-controls) Codes | ✅ | ✅ | ✅ |
| [Encoders](features/encoders.md) | ✅ | ✅ | ✅ |
| [Display Support](features/displays.md)[^1] | 🚧 | 🚧 | ✅ |
| [RGB Underglow](features/underglow.md) | ✅ | ✅ | ✅ |
| [Backlight](features/backlight.mdx) | ✅ | ✅ | ✅ |
| One Shot Keys | ✅ | ✅ | ✅ |
| [Combo Keys](keymaps/combos.md) | ✅ | | ✅ |
| [Macros](keymaps/behaviors/macros.md) | ✅ | ✅ | ✅ |
| Mouse Keys | 🚧 | ✅ | ✅ |
| Low Active Power Usage | ✅ | | |
| Low Power Sleep States | ✅ | ✅ | |
| [Low Power Mode (VCC Shutoff)](keymaps/behaviors/power.md) | ✅ | ✅ | |
| Battery Reporting | ✅ | ✅ | |
| Shell over BLE | 💡 | | |
| Realtime Keymap Updating | 💡 | | ✅ |
| AVR/8 Bit | | | ✅ |
| [Wide Range of ARM Chips Supported](https://docs.zephyrproject.org/3.5.0/boards/index.html) | ✅ | | |
[^2]: Tap-Dances are limited to single and double-tap on BlueMicro
[^1]: OLEDs are currently proof of concept in ZMK.

View file

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

View file

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