#pragma once #include "USBHID.h" #include // Absolute-position mouse HID device. // // The stock USBHIDMouse is RELATIVE (it reports dx/dy deltas), which // accumulates error and drifts the remote cursor out of sync over a lossy // BLE link. This device reports ABSOLUTE coordinates (0..32767 spanning the // target's screen) so every report fully specifies where the cursor is — a // dropped report just means the next one re-pins the position, never drift. // That's exactly what the BT Keyboard virtual trackpad needs to stay synced // across many devices. // // Report payload (the 1-byte report ID is prepended by USBHID::SendReport): // byte 0 : buttons bitmask (bit0 left, bit1 right, bit2 middle) // bytes 1-2 : X (uint16 little-endian, 0..32767) // bytes 3-4 : Y (uint16 little-endian, 0..32767) // byte 5 : wheel (int8, relative scroll tick) #define ABS_MOUSE_REPORT_ID 0x0A #define ABS_MOUSE_BTN_LEFT 0x01 #define ABS_MOUSE_BTN_RIGHT 0x02 #define ABS_MOUSE_BTN_MIDDLE 0x04 #define ABS_MOUSE_MAX 32767 class AbsoluteMouse : public USBHIDDevice { public: AbsoluteMouse() : _hid(), _buttons(0), _x(0), _y(0) { static bool initialized = false; if (!initialized) { initialized = true; uint16_t len = 0; _descriptor(&len); _hid.addDevice(this, len); } } void begin() { _hid.begin(); } // Called by the TinyUSB stack to fetch our report descriptor. uint16_t _onGetDescriptor(uint8_t* buffer) override { uint16_t len = 0; const uint8_t* d = _descriptor(&len); memcpy(buffer, d, len); return len; } bool ready() { return _hid.ready(); } // Emit one absolute report. ``x``/``y`` are 0..ABS_MOUSE_MAX; ``wheel`` // is a relative scroll tick. Returns the SendReport result. bool report(uint8_t buttons, uint16_t x, uint16_t y, int8_t wheel) { if (x > ABS_MOUSE_MAX) x = ABS_MOUSE_MAX; if (y > ABS_MOUSE_MAX) y = ABS_MOUSE_MAX; _buttons = buttons; _x = x; _y = y; uint8_t r[6]; r[0] = buttons; r[1] = (uint8_t)(x & 0xFF); r[2] = (uint8_t)((x >> 8) & 0xFF); r[3] = (uint8_t)(y & 0xFF); r[4] = (uint8_t)((y >> 8) & 0xFF); r[5] = (uint8_t)wheel; return _hid.SendReport(ABS_MOUSE_REPORT_ID, r, sizeof(r)); } private: USBHID _hid; uint8_t _buttons; uint16_t _x, _y; static const uint8_t* _descriptor(uint16_t* outLen) { static const uint8_t d[] = { 0x05, 0x01, // Usage Page (Generic Desktop) 0x09, 0x02, // Usage (Mouse) 0xA1, 0x01, // Collection (Application) 0x85, ABS_MOUSE_REPORT_ID, // Report ID 0x09, 0x01, // Usage (Pointer) 0xA1, 0x00, // Collection (Physical) 0x05, 0x09, // Usage Page (Button) 0x19, 0x01, // Usage Minimum (1) 0x29, 0x03, // Usage Maximum (3) 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) 0x95, 0x01, // Report Count (1) 0x75, 0x05, // Report Size (5) 0x81, 0x03, // Input (Const) - padding 0x05, 0x01, // Usage Page (Generic Desktop) 0x09, 0x30, // Usage (X) 0x09, 0x31, // Usage (Y) 0x16, 0x00, 0x00, // Logical Minimum (0) 0x26, 0xFF, 0x7F, // Logical Maximum (32767) 0x75, 0x10, // Report Size (16) 0x95, 0x02, // Report Count (2) 0x81, 0x02, // Input (Data,Var,Abs) 0x09, 0x38, // Usage (Wheel) 0x15, 0x81, // Logical Minimum (-127) 0x25, 0x7F, // Logical Maximum (127) 0x75, 0x08, // Report Size (8) 0x95, 0x01, // Report Count (1) 0x81, 0x06, // Input (Data,Var,Rel) 0xC0, // End Collection 0xC0 // End Collection }; *outLen = sizeof(d); return d; } };