Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(mouse): Added mouse emulation #778

Closed
wants to merge 30 commits into from
Closed
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
b14b024
Preliminary work for mouse click
krikun98 Apr 27, 2021
54ac765
Fine-tuning report, 16 buttons
krikun98 Apr 28, 2021
016256b
Bluetooth tuning, mouse wheel and movement backend
krikun98 Apr 29, 2021
3fb874f
Mouse-related behaviours
Apr 29, 2021
dc2e30d
Add mouse movement event
Apr 29, 2021
ee51487
Continuous mouse movement prototype
Apr 29, 2021
e2b97f1
Add mouse behaviour documentation
May 1, 2021
a339a9a
Cleaning out prototype traces
krikun98 May 2, 2021
141a525
clang-format
krikun98 May 2, 2021
345ef42
Implemented Rinh's suggestion to remove deadlocks
dtsykunov May 2, 2021
58178a7
Raised BLE mouse report queue size
krikun98 May 2, 2021
571e457
Documentation refactor
krikun98 May 2, 2021
0893c76
Add the doc page to the sidebar
krikun98 May 2, 2021
fddadb9
Added new mouse movement macros
krikun98 May 3, 2021
9957b28
Review edits: macro, event override fix, cosmetics
krikun98 May 3, 2021
6de29af
Mouse movement coordinate signedness consistency
krikun98 May 3, 2021
359f35b
Reverted mouse buttons 9-16
krikun98 May 3, 2021
4d08f97
Report refactor (added macros)
krikun98 May 3, 2021
b4ec49e
Simplify binary arithmetic
okke-formsma May 14, 2021
728f42e
Modified mouse_timer_unref to account for errors
krikun98 May 14, 2021
8fc5962
feat(mouse keys): add events, smoothing and acceleration
okke-formsma May 14, 2021
eb089b5
Added dedicated mouse work queue option
krikun98 Aug 26, 2021
96660dc
Simplified tick rate and made it configurable
krikun98 Aug 27, 2021
0bcd44a
Add messages to BLE queue without a waiting interval
krikun98 Aug 30, 2021
992bbc0
Cleanup and acceleration fixes
krikun98 Sep 2, 2021
8088585
Send mouse messages from dedicated thread
krikun98 Sep 21, 2021
cbce1db
Added documentation for new features
krikun98 Sep 23, 2021
ec85b7a
Moved tick duration
krikun98 Sep 23, 2021
2bf5e44
clang-format
krikun98 Sep 23, 2021
ee855f4
prettier
krikun98 Sep 23, 2021
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Next Next commit
Preliminary work for mouse click
  • Loading branch information
krikun98 committed Jan 28, 2022
commit b14b02453057246169030b748af41c44c5f92750
4 changes: 4 additions & 0 deletions app/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,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 5

config ZMK_BLE_CLEAR_BONDS_ON_START
bool "Configuration that clears all bond information from the keyboard on startup."
default n
Expand Down
9 changes: 9 additions & 0 deletions app/include/dt-bindings/zmk/mouse.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
/*
* Copyright (c) 2020 The ZMK Contributors
*
* SPDX-License-Identifier: MIT
*/
#pragma once

#include <dt-bindings/zmk/hid_usage.h>
#include <dt-bindings/zmk/hid_usage_pages.h>
46 changes: 46 additions & 0 deletions app/include/zmk/hid.h
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
#include <usb/class/usb_hid.h>

#include <zmk/keys.h>
#include <zmk/mouse.h>
#include <dt-bindings/zmk/hid_usage.h>
#include <dt-bindings/zmk/hid_usage_pages.h>

Expand Down Expand Up @@ -185,6 +186,33 @@ static const uint8_t zmk_hid_report_desc[] = {
0x00,
/* END COLLECTION */
HID_MI_COLLECTION_END,

0x05, 0x01, /* Usage Page (Generic Desktop Ctrls) */
0x09, 0x02, /* Usage (Mouse) */
0xA1, 0x01, /* Collection (Application) */
0x09, 0x01, /* Usage (Pointer) */
0xA1, 0x00, /* Collection (Physical) */
0x05, 0x09, /* Usage Page (Button) */
0x19, 0x01, /* Usage Minimum (0x01) */
0x29, 0x03, /* Usage Maximum (0x03) */
0x15, 0x00, /* Logical Minimum (0) */
0x25, 0x01, /* Logical Maximum (1) */
0x95, 0x03, /* Report Count (3) */
0x75, 0x01, /* Report Size (1) */
0x81, 0x02, /* Input (Data,Var,Abs,No Wrap,Linear,...) */
0x95, 0x01, /* Report Count (1) */
0x75, 0x05, /* Report Size (5) */
0x81, 0x03, /* Input (Const,Var,Abs,No Wrap,Linear,...) */
0x05, 0x01, /* Usage Page (Generic Desktop Ctrls) */
0x09, 0x30, /* Usage (X) */
0x09, 0x31, /* Usage (Y) */
0x15, 0x81, /* Logical Minimum (129) */
0x25, 0x7F, /* Logical Maximum (127) */
0x75, 0x08, /* Report Size (8) */
0x95, 0x02, /* Report Count (2) */
0x81, 0x06, /* Input (Data,Var,Rel,No Wrap,Linear,...) */
0xC0, /* End Collection */
0xC0, /* End Collection */
};

// struct zmk_hid_boot_report
Expand Down Expand Up @@ -222,6 +250,17 @@ 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;
int8_t x;
int8_t y;
} __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);
Expand All @@ -237,5 +276,12 @@ int zmk_hid_consumer_press(zmk_key_t key);
int zmk_hid_consumer_release(zmk_key_t key);
void zmk_hid_consumer_clear();

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_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();
1 change: 1 addition & 0 deletions app/include/zmk/hog.h
Original file line number Diff line number Diff line change
Expand Up @@ -13,3 +13,4 @@ int zmk_hog_init();

int zmk_hog_send_keyboard_report(struct zmk_hid_keyboard_report_body *body);
int zmk_hog_send_consumer_report(struct zmk_hid_consumer_report_body *body);
int zmk_hog_send_mouse_report(struct zmk_hid_mouse_report_body *body);
13 changes: 13 additions & 0 deletions app/include/zmk/mouse.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
/*
* Copyright (c) 2021 The ZMK Contributors
*
* SPDX-License-Identifier: MIT
*/

#pragma once

#include <zephyr.h>
#include <dt-bindings/zmk/mouse.h>

typedef uint8_t zmk_mouse_button_flags_t;
typedef uint8_t zmk_mouse_button_t;
38 changes: 36 additions & 2 deletions app/src/endpoints.c
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,7 @@ static int send_consumer_report() {

switch (current_endpoint) {
#if IS_ENABLED(CONFIG_ZMK_USB)
case ZMK_ENDPOINT_USB: {
case ZMK_ENDPOINT_USB: {
int err = zmk_usb_hid_send_report((uint8_t *)consumer_report, sizeof(*consumer_report));
if (err) {
LOG_ERR("FAILED TO SEND OVER USB: %d", err);
Expand All @@ -116,7 +116,7 @@ static int send_consumer_report() {
#endif /* IS_ENABLED(CONFIG_ZMK_USB) */

#if IS_ENABLED(CONFIG_ZMK_BLE)
case ZMK_ENDPOINT_BLE: {
case ZMK_ENDPOINT_BLE: {
int err = zmk_hog_send_consumer_report(&consumer_report->body);
if (err) {
LOG_ERR("FAILED TO SEND OVER HOG: %d", err);
Expand All @@ -131,6 +131,36 @@ static int send_consumer_report() {
}
}

static int 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: {
int err = zmk_hog_send_mouse_report(&mouse_report->body);
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;
}
}

int zmk_endpoints_send_report(uint16_t usage_page) {

LOG_DBG("usage page 0x%02X", usage_page);
Expand All @@ -139,6 +169,8 @@ int zmk_endpoints_send_report(uint16_t usage_page) {
return send_keyboard_report();
case HID_USAGE_CONSUMER:
return send_consumer_report();
case HID_USAGE_GD:
return send_mouse_report();
default:
LOG_ERR("Unsupported usage page %d", usage_page);
return -ENOTSUP;
Expand Down Expand Up @@ -229,9 +261,11 @@ static enum zmk_endpoint get_selected_endpoint() {
static void disconnect_current_endpoint() {
zmk_hid_keyboard_clear();
zmk_hid_consumer_clear();
zmk_hid_mouse_clear();

zmk_endpoints_send_report(HID_USAGE_KEY);
zmk_endpoints_send_report(HID_USAGE_CONSUMER);
zmk_endpoints_send_report(HID_USAGE_GD);
}

static void update_current_endpoint() {
Expand Down
62 changes: 62 additions & 0 deletions app/src/hid.c
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,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 = 3, .body = {
.buttons = 0, .x = 0, .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};
Expand Down Expand Up @@ -178,10 +181,69 @@ int zmk_hid_consumer_release(zmk_key_t code) {

void zmk_hid_consumer_clear() { memset(&consumer_report.body, 0, sizeof(consumer_report.body)); }


// Keep track of how often a button was pressed.
// Only release the button if the count is 0.
static int explicit_button_counts[3] = {0, 0, 0};
static zmk_mod_flags_t explicit_buttons = 0;

#define SET_MOUSE_BUTTONS(butts) \
{ \
mouse_report.body.buttons = butts; \
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-5]++;
LOG_DBG("Button %d count %d", button, explicit_button_counts[button-5]);
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-5] <= 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-5]);
if (explicit_button_counts[button-5] == 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 = 5; i < 8; 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 = 5; i < 8; i++) {
if (buttons & (1 << i)) {
zmk_hid_mouse_button_release(i);
}
}
return 0;
}

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;
}

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;
}
15 changes: 15 additions & 0 deletions app/src/hid_listener.c
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,13 @@ static int hid_listener_keycode_pressed(const struct zmk_keycode_state_changed *
return err;
}
break;
case HID_USAGE_GD:
err = zmk_hid_mouse_buttons_press(ev->keycode);
if (err) {
LOG_ERR("Unable to press button");
return err;
}
break;
}
explicit_mods_changed = zmk_hid_register_mods(ev->explicit_modifiers);
implicit_mods_changed = zmk_hid_implicit_modifiers_press(ev->implicit_modifiers);
Expand Down Expand Up @@ -70,6 +77,14 @@ static int hid_listener_keycode_released(const struct zmk_keycode_state_changed
LOG_ERR("Unable to release keycode");
return err;
}
break;
case HID_USAGE_GD:
err = zmk_hid_mouse_buttons_release(ev->keycode);
if (err) {
LOG_ERR("Unable to release button");
return err;
}
break;
}

explicit_mods_changed = zmk_hid_unregister_mods(ev->explicit_modifiers);
Expand Down
71 changes: 71 additions & 0 deletions app/src/hog.c
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,11 @@ static struct hids_report consumer_input = {
.type = HIDS_INPUT,
};

static struct hids_report mouse_input = {
.id = 0x03,
.type = HIDS_INPUT,
};

static bool host_requests_notification = false;
static uint8_t ctrl_point;
// static uint8_t proto_mode;
Expand Down Expand Up @@ -93,6 +98,14 @@ 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,
Expand Down Expand Up @@ -139,6 +152,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));

Expand Down Expand Up @@ -261,6 +281,57 @@ 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[10],
.data = &report,
.len = sizeof(report),
};

int err = bt_gatt_notify_cb(conn, &notify_params);
if (err) {
LOG_DBG("Error notifying %d", err);
}

bt_conn_unref(conn);
}
};

K_WORK_DEFINE(hog_mouse_work, send_mouse_report_callback);

int zmk_hog_send_mouse_report(struct zmk_hid_mouse_report_body *report) {
int err = k_msgq_put(&zmk_hog_mouse_msgq, report, K_MSEC(100));
if (err) {
switch (err) {
case -EAGAIN: {
LOG_WRN("Mouse message queue full, popping first message and queueing again");
struct zmk_hid_mouse_report_body discarded_report;
k_msgq_get(&zmk_hog_mouse_msgq, &discarded_report, K_NO_WAIT);
return zmk_hog_send_mouse_report(report);
}
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_init(const struct device *_arg) {
k_work_q_start(&hog_work_q, hog_q_stack, K_THREAD_STACK_SIZEOF(hog_q_stack),
CONFIG_ZMK_BLE_THREAD_PRIORITY);
Expand Down