From 91a63554dcc9d60cdead1953a86c75c049878432 Mon Sep 17 00:00:00 2001 From: Benedek Kupper Date: Thu, 19 Dec 2024 22:18:03 +0100 Subject: [PATCH] add high resolution scrolling to UHK-80, switch to standard defined implementation --- device/src/usb/mouse_app.cpp | 21 ++++++++ device/src/usb/mouse_app.hpp | 47 +++++++++-------- device/src/usb/report_ids.h | 2 + device/src/usb/usb_compatibility.cpp | 12 ++++- right/src/mouse_controller.c | 5 +- right/src/mouse_keys.c | 7 ++- .../usb_descriptor_mouse_report.h | 50 +++++++++---------- .../src/usb_interfaces/usb_interface_mouse.c | 35 +++++++------ .../src/usb_interfaces/usb_interface_mouse.h | 3 +- west.yml | 2 +- 10 files changed, 110 insertions(+), 74 deletions(-) diff --git a/device/src/usb/mouse_app.cpp b/device/src/usb/mouse_app.cpp index 352d19383..c2a0759d5 100644 --- a/device/src/usb/mouse_app.cpp +++ b/device/src/usb/mouse_app.cpp @@ -1,4 +1,5 @@ #include "mouse_app.hpp" +#include "app_base.hpp" #include "zephyr/sys/printk.h" mouse_app &mouse_app::handle() @@ -11,9 +12,29 @@ void mouse_app::start(hid::protocol prot) { // TODO start handling mouse events report_buffer_ = {}; + resolution_buffer_ = {}; + receive_report(&resolution_buffer_); } void mouse_app::set_report_state(const mouse_report_base<> &data) { send({data.data(), sizeof(data)}); } + +void mouse_app::set_report(hid::report::type type, const std::span &data) +{ + if (hid::report::selector(type, data.front()) != resolution_buffer_.selector()) { + return; + } + resolution_buffer_ = *reinterpret_cast(data.data()); + receive_report(&resolution_buffer_); +} + +void mouse_app::get_report(hid::report::selector select, const std::span &buffer) +{ + if (select == resolution_buffer_.selector()) { + send_report(&resolution_buffer_); + return; + } + app_base::get_report(select, buffer); +} diff --git a/device/src/usb/mouse_app.hpp b/device/src/usb/mouse_app.hpp index 87c8259b7..f52da5fee 100644 --- a/device/src/usb/mouse_app.hpp +++ b/device/src/usb/mouse_app.hpp @@ -18,10 +18,15 @@ enum class mouse_button { _8 }; +template +using limit_fitted_int = + std::conditional_t<(LIMIT > std::numeric_limits::max()), hid::le_int16_t, int8_t>; + class mouse_app : public app_base { static constexpr auto LAST_BUTTON = hid::page::button(20); static constexpr int16_t AXIS_LIMIT = 4096; - static constexpr int8_t WHEEL_LIMIT = 127; + static constexpr int16_t MAX_SCROLL_RESOLUTION = 120; + static constexpr int16_t WHEEL_LIMIT = 32767; public: static constexpr auto report_desc() @@ -48,27 +53,12 @@ class mouse_app : public app_base { // relative X,Y directions usage(generic_desktop::X), usage(generic_desktop::Y), - logical_limits<2, 2>(-AXIS_LIMIT, AXIS_LIMIT), + logical_limits<(AXIS_LIMIT > std::numeric_limits::max() ? 2 : 1)>(-AXIS_LIMIT, AXIS_LIMIT), report_count(2), - report_size(16), + report_size(AXIS_LIMIT > std::numeric_limits::max() ? 16 : 8), input::relative_variable(), - // vertical wheel - collection::logical( - usage(generic_desktop::WHEEL), - logical_limits<1, 1>(-WHEEL_LIMIT, WHEEL_LIMIT), - report_count(1), - report_size(8), - input::relative_variable() - ), - // horizontal wheel - collection::logical( - usage_extended(consumer::AC_PAN), - logical_limits<1, 1>(-WHEEL_LIMIT, WHEEL_LIMIT), - report_count(1), - report_size(8), - input::relative_variable() - ) + hid::app::mouse::high_resolution_scrolling() ) ) ); @@ -79,10 +69,10 @@ class mouse_app : public app_base { struct mouse_report_base : public hid::report::base { hid::report_bitset buttons{}; - hid::le_int16_t x{}; - hid::le_int16_t y{}; - int8_t wheel_y{}; - int8_t wheel_x{}; + limit_fitted_int x{}; + limit_fitted_int y{}; + limit_fitted_int wheel_y{}; + limit_fitted_int wheel_x{}; constexpr mouse_report_base() = default; @@ -98,9 +88,18 @@ class mouse_app : public app_base { mouse_app() : app_base(this, report_buffer_) {} void start(hid::protocol prot) override; + void set_report(hid::report::type type, const std::span &data) override; + void get_report(hid::report::selector select, const std::span &buffer) override; using mouse_report = mouse_report_base; - C2USB_USB_TRANSFER_ALIGN(mouse_report, report_buffer_){}; + C2USB_USB_TRANSFER_ALIGN(mouse_report, report_buffer_) {}; + using scroll_resolution_report = + hid::app::mouse::resolution_multiplier_report; + C2USB_USB_TRANSFER_ALIGN(scroll_resolution_report, resolution_buffer_) {}; + + public: + const auto &resolution_report() const { return resolution_buffer_; } }; using mouse_buffer = mouse_app::mouse_report_base<>; diff --git a/device/src/usb/report_ids.h b/device/src/usb/report_ids.h index 04be7e76e..1ba5e5509 100644 --- a/device/src/usb/report_ids.h +++ b/device/src/usb/report_ids.h @@ -13,6 +13,8 @@ enum report_ids { // OUT OUT_KEYBOARD_LEDS = 1, OUT_COMMAND = 4, + // FEATURE + FEATURE_MOUSE = 3, }; #endif // __REPORT_IDS__ \ No newline at end of file diff --git a/device/src/usb/usb_compatibility.cpp b/device/src/usb/usb_compatibility.cpp index 02f0016c9..964440c5e 100644 --- a/device/src/usb/usb_compatibility.cpp +++ b/device/src/usb/usb_compatibility.cpp @@ -4,8 +4,8 @@ extern "C" { #include "debug.h" #include "event_scheduler.h" #include "key_states.h" -#include "macro_events.h" #include "link_protocol.h" +#include "macro_events.h" #include "messenger.h" #include "nus_server.h" #include "state_sync.h" @@ -114,3 +114,13 @@ extern "C" void UsbCompatibility_SetKeyboardLedsState(bool capsLock, bool numLoc StateSync_UpdateProperty(StateSyncPropertyId_KeyboardLedsState, NULL); } + +extern "C" float VerticalScrollMultiplier(void) +{ + return mouse_app::handle().resolution_report().vertical_scroll_multiplier(); +} + +extern "C" float HorizontalScrollMultiplier(void) +{ + return mouse_app::handle().resolution_report().horizontal_scroll_multiplier(); +} diff --git a/right/src/mouse_controller.c b/right/src/mouse_controller.c index f1e9f4102..b0176204b 100644 --- a/right/src/mouse_controller.c +++ b/right/src/mouse_controller.c @@ -551,13 +551,12 @@ static void processModuleKineticState( break; } case NavigationMode_Scroll: { - speed *= UsbMouseScrollMultiplier; if (!moduleConfiguration->scrollAxisLock) { float xIntegerPart; float yIntegerPart; - ks->xFractionRemainder = modff(ks->xFractionRemainder + x * speed / moduleConfiguration->scrollSpeedDivisor, &xIntegerPart); - ks->yFractionRemainder = modff(ks->yFractionRemainder + y * speed / moduleConfiguration->scrollSpeedDivisor, &yIntegerPart); + ks->xFractionRemainder = modff(ks->xFractionRemainder + x * speed * HorizontalScrollMultiplier() / moduleConfiguration->scrollSpeedDivisor, &xIntegerPart); + ks->yFractionRemainder = modff(ks->yFractionRemainder + y * speed * VerticalScrollMultiplier() / moduleConfiguration->scrollSpeedDivisor, &yIntegerPart); MouseControllerMouseReport.wheelX += xInversion*xIntegerPart; MouseControllerMouseReport.wheelY += yInversion*yIntegerPart; diff --git a/right/src/mouse_keys.c b/right/src/mouse_keys.c index 8a2710f6d..ab2bad14c 100644 --- a/right/src/mouse_keys.c +++ b/right/src/mouse_keys.c @@ -8,6 +8,7 @@ #include "config_manager.h" #include "event_scheduler.h" #include +#include static uint32_t mouseUsbReportUpdateTime = 0; static uint32_t mouseElapsedTime; @@ -66,7 +67,11 @@ void MouseKeys_ActivateDirectionSigns(uint8_t state) { static void processMouseKineticState(mouse_kinetic_state_t *kineticState) { - int16_t scrollMultiplier = kineticState->isScroll ? UsbMouseScrollMultiplier : 1; + float scrollMultiplier = 1.f; + if (kineticState->isScroll) { + // in practice the vertical and horizontal scroll multipliers are always the same + scrollMultiplier = VerticalScrollMultiplier(); + } float initialSpeed = scrollMultiplier * kineticState->intMultiplier * kineticState->initialSpeed; float acceleration = scrollMultiplier * kineticState->intMultiplier * kineticState->acceleration; float deceleratedSpeed = scrollMultiplier * kineticState->intMultiplier * kineticState->deceleratedSpeed; diff --git a/right/src/usb_descriptors/usb_descriptor_mouse_report.h b/right/src/usb_descriptors/usb_descriptor_mouse_report.h index 464e6d697..eae03bdac 100644 --- a/right/src/usb_descriptors/usb_descriptor_mouse_report.h +++ b/right/src/usb_descriptors/usb_descriptor_mouse_report.h @@ -61,25 +61,6 @@ HID_RI_INPUT(8, HID_IOF_DATA | HID_IOF_VARIABLE | HID_IOF_RELATIVE), HID_RI_COLLECTION(8, HID_RI_COLLECTION_LOGICAL), - - // Scroll wheels - - // Resolution multiplier for high-res scroll support - // To have a multiplier apply to a wheel, it must be in the - // same logical collection as the wheel, or else there must - // be no logical collections (according to the USB HID spec); - // so to have a single multiplier apply to the two wheels, - // they must be in the same logical collection (or there - // must be no logical collection at all) - HID_RI_USAGE(8, HID_RI_USAGE_GENERIC_DESKTOP_RESOLUTION_MULTIPLIER), - HID_RI_LOGICAL_MINIMUM(8, 0), - HID_RI_LOGICAL_MAXIMUM(8, 1), - HID_RI_PHYSICAL_MINIMUM(8, USB_MOUSE_REPORT_DESCRIPTOR_MIN_RESOLUTION_MULTIPLIER_PHYSICAL_VALUE), - HID_RI_PHYSICAL_MAXIMUM(8, USB_MOUSE_REPORT_DESCRIPTOR_MAX_RESOLUTION_MULTIPLIER_PHYSICAL_VALUE), - HID_RI_REPORT_COUNT(8, 1), - HID_RI_REPORT_SIZE(8, 8), - HID_RI_FEATURE(8, HID_IOF_DATA | HID_IOF_VARIABLE | HID_IOF_ABSOLUTE), - // Vertical wheel HID_RI_USAGE(8, HID_RI_USAGE_GENERIC_DESKTOP_WHEEL), HID_RI_LOGICAL_MINIMUM(16, -32767), @@ -90,18 +71,37 @@ HID_RI_REPORT_SIZE(8, 16), HID_RI_INPUT(8, HID_IOF_DATA | HID_IOF_VARIABLE | HID_IOF_RELATIVE), + HID_RI_USAGE(8, HID_RI_USAGE_GENERIC_DESKTOP_RESOLUTION_MULTIPLIER), + HID_RI_PUSH(0), + HID_RI_LOGICAL_MINIMUM(8, 0), + HID_RI_LOGICAL_MAXIMUM(8, 1), + HID_RI_PHYSICAL_MINIMUM(8, USB_MOUSE_REPORT_DESCRIPTOR_MIN_RESOLUTION_MULTIPLIER_PHYSICAL_VALUE), + HID_RI_PHYSICAL_MAXIMUM(8, USB_MOUSE_REPORT_DESCRIPTOR_MAX_RESOLUTION_MULTIPLIER_PHYSICAL_VALUE), + HID_RI_REPORT_COUNT(8, 1), + HID_RI_REPORT_SIZE(8, 2), + HID_RI_FEATURE(8, HID_IOF_DATA | HID_IOF_VARIABLE | HID_IOF_ABSOLUTE), + HID_RI_POP(0), + HID_RI_END_COLLECTION(0), + + HID_RI_COLLECTION(8, HID_RI_COLLECTION_LOGICAL), // Horizontal wheel HID_RI_USAGE_PAGE(8, HID_RI_USAGE_PAGE_CONSUMER), HID_RI_USAGE(16, HID_RI_USAGE_CONSUMER_AC_PAN), - HID_RI_LOGICAL_MINIMUM(16, -32767), - HID_RI_LOGICAL_MAXIMUM(16, 32767), - HID_RI_PHYSICAL_MINIMUM(16, -32767), - HID_RI_PHYSICAL_MAXIMUM(16, 32767), - HID_RI_REPORT_COUNT(8, 1), - HID_RI_REPORT_SIZE(8, 16), HID_RI_INPUT(8, HID_IOF_DATA | HID_IOF_VARIABLE | HID_IOF_RELATIVE), + HID_RI_USAGE(8, HID_RI_USAGE_GENERIC_DESKTOP_RESOLUTION_MULTIPLIER), + HID_RI_PUSH(0), + HID_RI_LOGICAL_MINIMUM(8, 0), + HID_RI_LOGICAL_MAXIMUM(8, 1), + HID_RI_PHYSICAL_MINIMUM(8, USB_MOUSE_REPORT_DESCRIPTOR_MIN_RESOLUTION_MULTIPLIER_PHYSICAL_VALUE), + HID_RI_PHYSICAL_MAXIMUM(8, USB_MOUSE_REPORT_DESCRIPTOR_MAX_RESOLUTION_MULTIPLIER_PHYSICAL_VALUE), + HID_RI_REPORT_COUNT(8, 1), + HID_RI_REPORT_SIZE(8, 2), + HID_RI_FEATURE(8, HID_IOF_DATA | HID_IOF_VARIABLE | HID_IOF_ABSOLUTE), + HID_RI_POP(0), HID_RI_END_COLLECTION(0), + HID_RI_REPORT_SIZE(8, 4), + HID_RI_FEATURE(8, HID_IOF_CONSTANT), HID_RI_END_COLLECTION(0), HID_RI_END_COLLECTION(0) diff --git a/right/src/usb_interfaces/usb_interface_mouse.c b/right/src/usb_interfaces/usb_interface_mouse.c index b6ca9edf3..ace4353ac 100644 --- a/right/src/usb_interfaces/usb_interface_mouse.c +++ b/right/src/usb_interfaces/usb_interface_mouse.c @@ -11,13 +11,10 @@ #include "usb_descriptors/usb_descriptor_mouse_report.h" static usb_mouse_report_t usbMouseReports[2]; -static uint8_t usbMouseFeatBuffer[USB_MOUSE_FEAT_REPORT_LENGTH]; usb_hid_protocol_t usbMouseProtocol; uint32_t UsbMouseActionCounter; usb_mouse_report_t* ActiveUsbMouseReport = usbMouseReports; -int16_t UsbMouseScrollMultiplier = USB_MOUSE_REPORT_DESCRIPTOR_MIN_RESOLUTION_MULTIPLIER_PHYSICAL_VALUE; - static usb_mouse_report_t* GetInactiveUsbMouseReport(void) { return ActiveUsbMouseReport == usbMouseReports ? usbMouseReports+1 : usbMouseReports; @@ -35,6 +32,8 @@ static void SwitchActiveUsbMouseReport(void) #ifndef __ZEPHYR__ +static uint8_t usbMouseFeatBuffer[USB_MOUSE_FEAT_REPORT_LENGTH]; + usb_hid_protocol_t UsbMouseGetProtocol(void) { return usbMouseProtocol; @@ -60,6 +59,18 @@ usb_status_t UsbMouseAction(void) return usb_status; } +static uint8_t scrollMultipliers = 0; + +float VerticalScrollMultiplier(void) +{ + return scrollMultipliers & 0x01 ? USB_MOUSE_REPORT_DESCRIPTOR_MAX_RESOLUTION_MULTIPLIER_PHYSICAL_VALUE : USB_MOUSE_REPORT_DESCRIPTOR_MIN_RESOLUTION_MULTIPLIER_PHYSICAL_VALUE; +} + +float HorizontalScrollMultiplier(void) +{ + return scrollMultipliers & 0x04 ? USB_MOUSE_REPORT_DESCRIPTOR_MAX_RESOLUTION_MULTIPLIER_PHYSICAL_VALUE : USB_MOUSE_REPORT_DESCRIPTOR_MIN_RESOLUTION_MULTIPLIER_PHYSICAL_VALUE; +} + usb_status_t UsbMouseCallback(class_handle_t handle, uint32_t event, void *param) { usb_device_hid_struct_t *hidHandle = (usb_device_hid_struct_t *)handle; @@ -67,6 +78,7 @@ usb_status_t UsbMouseCallback(class_handle_t handle, uint32_t event, void *param switch (event) { case ((uint32_t)-kUSB_DeviceEventSetConfiguration): + scrollMultipliers = 0; error = kStatus_USB_Success; break; case ((uint32_t)-kUSB_DeviceEventSetInterface): @@ -92,7 +104,7 @@ usb_status_t UsbMouseCallback(class_handle_t handle, uint32_t event, void *param SwitchActiveUsbMouseReport(); error = kStatus_USB_Success; } else if (report->reportType == USB_DEVICE_HID_REQUEST_GET_REPORT_TYPE_FEATURE) { - usbMouseFeatBuffer[0] = (uint8_t)(UsbMouseScrollMultiplier != USB_MOUSE_REPORT_DESCRIPTOR_MIN_RESOLUTION_MULTIPLIER_PHYSICAL_VALUE); + usbMouseFeatBuffer[0] = scrollMultipliers; report->reportBuffer = usbMouseFeatBuffer; report->reportLength = sizeof(usbMouseFeatBuffer); error = kStatus_USB_Success; @@ -105,12 +117,7 @@ usb_status_t UsbMouseCallback(class_handle_t handle, uint32_t event, void *param case kUSB_DeviceHidEventSetReport: { usb_device_hid_report_struct_t *report = (usb_device_hid_report_struct_t*)param; if (report->reportType == USB_DEVICE_HID_REQUEST_GET_REPORT_TYPE_FEATURE && report->reportId == 0 && report->reportLength <= sizeof(usbMouseFeatBuffer)) { - // With a single resolution multiplier, this case will never be - // hit on Linux (for multiple resolution multipliers, one value - // will be missing, so would have to be inferred from the - // other(s)). But Windows does use this request properly, so it - // needs to be handled appropriately. - UsbMouseScrollMultiplier = usbMouseFeatBuffer[0] ? USB_MOUSE_REPORT_DESCRIPTOR_MAX_RESOLUTION_MULTIPLIER_PHYSICAL_VALUE : USB_MOUSE_REPORT_DESCRIPTOR_MIN_RESOLUTION_MULTIPLIER_PHYSICAL_VALUE; + scrollMultipliers = usbMouseFeatBuffer[0]; error = kStatus_USB_Success; } else { error = kStatus_USB_InvalidRequest; @@ -121,14 +128,6 @@ usb_status_t UsbMouseCallback(class_handle_t handle, uint32_t event, void *param case kUSB_DeviceHidEventRequestReportBuffer: { usb_device_hid_report_struct_t *report = (usb_device_hid_report_struct_t*)param; if (report->reportType == USB_DEVICE_HID_REQUEST_GET_REPORT_TYPE_FEATURE && report->reportId == 0 && report->reportLength <= sizeof(usbMouseFeatBuffer)) { - // The Linux implementation of SetReport when initializing a - // device with a single resolution multiplier value is broken, - // sending an empty report, and as a result the - // kUSB_DeviceHidEventSetReport case above isn't triggered at - // all; but it only sends this report when it detects the - // resolution multiplier, and the intention is to activate the - // feature, so turn high-res mode on here. - UsbMouseScrollMultiplier = USB_MOUSE_REPORT_DESCRIPTOR_MAX_RESOLUTION_MULTIPLIER_PHYSICAL_VALUE; report->reportBuffer = usbMouseFeatBuffer; error = kStatus_USB_Success; } else { diff --git a/right/src/usb_interfaces/usb_interface_mouse.h b/right/src/usb_interfaces/usb_interface_mouse.h index fd2fe0b8b..eba52a35a 100644 --- a/right/src/usb_interfaces/usb_interface_mouse.h +++ b/right/src/usb_interfaces/usb_interface_mouse.h @@ -38,7 +38,6 @@ // Variables: - extern int16_t UsbMouseScrollMultiplier; extern uint32_t UsbMouseActionCounter; extern usb_mouse_report_t* ActiveUsbMouseReport; @@ -50,6 +49,8 @@ usb_hid_protocol_t UsbMouseGetProtocol(void); #endif + float VerticalScrollMultiplier(void); + float HorizontalScrollMultiplier(void); void UsbMouseResetActiveReport(void); void UsbMouseSendActiveReport(void); usb_status_t UsbMouseCheckIdleElapsed(); diff --git a/west.yml b/west.yml index 6aecb198f..f5f8c38fc 100644 --- a/west.yml +++ b/west.yml @@ -36,5 +36,5 @@ manifest: - name: c2usb remote: IntergatedCircuits - revision: da4ad86f1cd3a4f8b42aa2d9acdd43e384341ce4 + revision: 5af7af626bd82c3937ca16395d6777259effa3fc clone-depth: 1