feat(mouse): Add mouse move and scroll support (#2477)

* feat(mouse): Add mouse move and scroll support

    * Use Zephyr input subsystem for all pointers.
    * Input processors for modifying events, e.g. scaling, swapping
      codes, temporary (mouse) layers, etc.
    * Mouse move/scroll behaviors.
    * Infrastructure in place for physical pointer input devices.

* feat: Add input split support.

* docs: Add initial pointer docs.

---------

Co-authored-by: Cem Aksoylar <caksoylar@users.noreply.github.com>
Co-authored-by: Alexander Krikun <krikun98@gmail.com>
Co-authored-by: Robert U <urob@users.noreply.github.com>
Co-authored-by: Shawn Meier <ftc@users.noreply.github.com>
Co-authored-by: Chris Andreae <chris@andreae.gen.nz>
Co-authored-by: Anant Thazhemadam <47104651+thazhemadam@users.noreply.github.com>
Co-authored-by: Erik Tollerud <erik.tollerud@gmail.com>
Co-authored-by: Nicolas Munnich <98408764+Nick-Munnich@users.noreply.github.com>
This commit is contained in:
Pete Johanson 2024-12-09 17:45:41 -07:00 committed by GitHub
parent 7e8c542c94
commit 6b40bfda53
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
119 changed files with 4223 additions and 229 deletions

View file

@ -7,6 +7,7 @@
#include <zephyr/drivers/sensor.h>
#include <zephyr/types.h>
#include <zephyr/sys/util.h>
#include <zephyr/sys/byteorder.h>
#include <zephyr/init.h>
#include <zephyr/logging/log.h>
@ -175,6 +176,45 @@ static ssize_t split_svc_get_selected_phys_layout(struct bt_conn *conn,
return bt_gatt_attr_read(conn, attrs, buf, len, offset, &selected, sizeof(selected));
}
#if IS_ENABLED(CONFIG_ZMK_INPUT_SPLIT)
static void split_input_events_ccc(const struct bt_gatt_attr *attr, uint16_t value) {
LOG_DBG("value %d", value);
}
// Duplicated from Zephyr, since it is internal there
struct gatt_cpf {
uint8_t format;
int8_t exponent;
uint16_t unit;
uint8_t name_space;
uint16_t description;
} __packed;
ssize_t bt_gatt_attr_read_input_split_cpf(struct bt_conn *conn, const struct bt_gatt_attr *attr,
void *buf, uint16_t len, uint16_t offset) {
uint16_t reg = (uint16_t)(uint32_t)attr->user_data;
struct gatt_cpf value;
value.format = 0x1B; // Struct
value.exponent = 0;
value.unit = sys_cpu_to_le16(0x2700); // Unitless
value.name_space = 0x01; // Bluetooth SIG
value.description = sys_cpu_to_le16(reg);
return bt_gatt_attr_read(conn, attr, buf, len, offset, &value, sizeof(value));
}
#define INPUT_SPLIT_CHARS(node_id) \
BT_GATT_CHARACTERISTIC(BT_UUID_DECLARE_128(ZMK_SPLIT_BT_INPUT_EVENT_UUID), \
BT_GATT_CHRC_NOTIFY, BT_GATT_PERM_READ_ENCRYPT, NULL, NULL, NULL), \
BT_GATT_CCC(split_input_events_ccc, \
BT_GATT_PERM_READ_ENCRYPT | BT_GATT_PERM_WRITE_ENCRYPT), \
BT_GATT_DESCRIPTOR(BT_UUID_GATT_CPF, BT_GATT_PERM_READ, bt_gatt_attr_read_input_split_cpf, \
NULL, (void *)DT_REG_ADDR(node_id)),
#endif
BT_GATT_SERVICE_DEFINE(
split_svc, BT_GATT_PRIMARY_SERVICE(BT_UUID_DECLARE_128(ZMK_SPLIT_BT_SERVICE_UUID)),
BT_GATT_CHARACTERISTIC(BT_UUID_DECLARE_128(ZMK_SPLIT_BT_CHAR_POSITION_STATE_UUID),
@ -192,10 +232,11 @@ BT_GATT_SERVICE_DEFINE(
split_svc_sensor_state, NULL, &last_sensor_event),
BT_GATT_CCC(split_svc_sensor_state_ccc, BT_GATT_PERM_READ_ENCRYPT | BT_GATT_PERM_WRITE_ENCRYPT),
#endif /* ZMK_KEYMAP_HAS_SENSORS */
DT_FOREACH_STATUS_OKAY(zmk_input_split, INPUT_SPLIT_CHARS)
#if IS_ENABLED(CONFIG_ZMK_SPLIT_PERIPHERAL_HID_INDICATORS)
BT_GATT_CHARACTERISTIC(BT_UUID_DECLARE_128(ZMK_SPLIT_BT_UPDATE_HID_INDICATORS_UUID),
BT_GATT_CHRC_WRITE_WITHOUT_RESP, BT_GATT_PERM_WRITE_ENCRYPT, NULL,
split_svc_update_indicators, NULL),
BT_GATT_CHARACTERISTIC(BT_UUID_DECLARE_128(ZMK_SPLIT_BT_UPDATE_HID_INDICATORS_UUID),
BT_GATT_CHRC_WRITE_WITHOUT_RESP, BT_GATT_PERM_WRITE_ENCRYPT, NULL,
split_svc_update_indicators, NULL),
#endif // IS_ENABLED(CONFIG_ZMK_SPLIT_PERIPHERAL_HID_INDICATORS)
BT_GATT_CHARACTERISTIC(BT_UUID_DECLARE_128(ZMK_SPLIT_BT_SELECT_PHYS_LAYOUT_UUID),
BT_GATT_CHRC_WRITE | BT_GATT_CHRC_READ,
@ -306,6 +347,29 @@ int zmk_split_bt_sensor_triggered(uint8_t sensor_index,
}
#endif /* ZMK_KEYMAP_HAS_SENSORS */
#if IS_ENABLED(CONFIG_ZMK_INPUT_SPLIT)
int zmk_split_bt_report_input(uint8_t reg, uint8_t type, uint16_t code, int32_t value, bool sync) {
for (size_t i = 0; i < split_svc.attr_count; i++) {
if (bt_uuid_cmp(split_svc.attrs[i].uuid,
BT_UUID_DECLARE_128(ZMK_SPLIT_BT_INPUT_EVENT_UUID)) == 0 &&
(uint8_t)(uint32_t)split_svc.attrs[i + 2].user_data == reg) {
struct zmk_split_input_event_payload payload = {
.type = type,
.code = code,
.value = value,
.sync = sync ? 1 : 0,
};
return bt_gatt_notify(NULL, &split_svc.attrs[i], &payload, sizeof(payload));
}
}
return -ENODEV;
}
#endif /* IS_ENABLED(CONFIG_ZMK_INPUT_SPLIT) */
static int service_init(void) {
static const struct k_work_queue_config queue_config = {
.name = "Split Peripheral Notification Queue"};