From c621251ad43e9178921e9cbebdd3d8d752fb634c Mon Sep 17 00:00:00 2001 From: Benedek Kupper Date: Thu, 19 Dec 2024 22:18:03 +0100 Subject: [PATCH 1/7] 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 | 38 +++++++------- .../src/usb_interfaces/usb_interface_mouse.h | 3 +- west.yml | 2 +- 10 files changed, 110 insertions(+), 77 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 ee366d0f7..aeae3ad05 100644 --- a/right/src/usb_interfaces/usb_interface_mouse.c +++ b/right/src/usb_interfaces/usb_interface_mouse.c @@ -13,16 +13,10 @@ static usb_mouse_report_t usbMouseReports[2]; -#ifndef __ZEPHYR__ -static uint8_t usbMouseFeatBuffer[USB_MOUSE_FEAT_REPORT_LENGTH]; -#endif - 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; @@ -40,6 +34,8 @@ static void SwitchActiveUsbMouseReport(void) #ifndef __ZEPHYR__ +static uint8_t usbMouseFeatBuffer[USB_MOUSE_FEAT_REPORT_LENGTH]; + usb_hid_protocol_t UsbMouseGetProtocol(void) { return usbMouseProtocol; @@ -65,6 +61,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; @@ -72,6 +80,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): @@ -97,7 +106,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; @@ -110,12 +119,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; @@ -126,14 +130,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 From 9f1f69c5076fe1f70d14e2e1eb99fef3c1d13332 Mon Sep 17 00:00:00 2001 From: Karel Tucek Date: Sat, 11 Jan 2025 23:24:05 +0100 Subject: [PATCH 2/7] Fix high-ress scrolling multiplier computations. --- device/src/usb/usb_compatibility.cpp | 4 ++-- right/src/mouse_controller.c | 10 ++++++++-- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/device/src/usb/usb_compatibility.cpp b/device/src/usb/usb_compatibility.cpp index 77fc1f106..32268eebd 100644 --- a/device/src/usb/usb_compatibility.cpp +++ b/device/src/usb/usb_compatibility.cpp @@ -229,10 +229,10 @@ extern "C" void UsbCompatibility_SetKeyboardLedsState(connection_id_t connection extern "C" float VerticalScrollMultiplier(void) { - return mouse_app::handle().resolution_report().vertical_scroll_multiplier(); + return mouse_app::usb_handle().resolution_report().vertical_scroll_multiplier(); } extern "C" float HorizontalScrollMultiplier(void) { - return mouse_app::handle().resolution_report().horizontal_scroll_multiplier(); + return mouse_app::usb_handle().resolution_report().horizontal_scroll_multiplier(); } diff --git a/right/src/mouse_controller.c b/right/src/mouse_controller.c index b0176204b..6ec175dc1 100644 --- a/right/src/mouse_controller.c +++ b/right/src/mouse_controller.c @@ -428,8 +428,14 @@ static void processAxisLocking( caretYModeMultiplier = ks->caretAxis == CaretAxis_Vertical ? 1.0f : axisLockSkew; } - ks->xFractionRemainder += x * speed / speedDivisor * caretXModeMultiplier; - ks->yFractionRemainder += y * speed / speedDivisor * caretYModeMultiplier; + float scrollMultiplier = 1.0f; + if (ks->currentNavigationMode == NavigationMode_Scroll) { + scrollMultiplier = VerticalScrollMultiplier(); + } + + ks->xFractionRemainder += x * speed / speedDivisor * scrollMultiplier * caretXModeMultiplier; + ks->yFractionRemainder += y * speed / speedDivisor * scrollMultiplier * caretYModeMultiplier; + // Start a new action (new "tick"), unless there is an action in progress. if (!caretModeActionIsRunning(ks)) { From 0e30487f6c5d7f176b42a11f45ce019ec9a109d2 Mon Sep 17 00:00:00 2001 From: Karel Tucek Date: Tue, 14 Jan 2025 11:49:30 +0100 Subject: [PATCH 3/7] Use correct scroll multipliers with dongles. --- device/src/state_sync.c | 10 ++++++++++ device/src/state_sync.h | 7 +++++++ device/src/usb/mouse_app.cpp | 11 ++++++++++ device/src/usb/usb_compatibility.cpp | 30 ++++++++++++++++++++++++++-- 4 files changed, 56 insertions(+), 2 deletions(-) diff --git a/device/src/state_sync.c b/device/src/state_sync.c index 512b68abf..0f2949160 100644 --- a/device/src/state_sync.c +++ b/device/src/state_sync.c @@ -46,6 +46,8 @@ static k_tid_t stateSyncThreadDongleId = 0; sync_generic_half_state_t SyncLeftHalfState; sync_generic_half_state_t SyncRightHalfState; +scroll_multipliers_t DongleScrollMultipliers = {1, 1}; + static void receiveProperty(device_id_t src, state_sync_prop_id_t property, const uint8_t *data, uint8_t len); #define DEFAULT_LAYER_PROP(NAME) \ @@ -134,6 +136,7 @@ static state_sync_prop_t stateSyncProps[StateSyncPropertyId_Count] = { CUSTOM(Config, SyncDirection_RightToLeft, DirtyState_Clean), CUSTOM(SwitchTestMode, SyncDirection_RightToLeft, DirtyState_Clean), SIMPLE(DongleStandby, SyncDirection_RightToDongle, DirtyState_Clean, &DongleStandby), + SIMPLE(DongleScrollMultipliers, SyncDirection_DongleToRight, DirtyState_Clean, &DongleScrollMultipliers), }; @@ -373,6 +376,11 @@ static void receiveProperty(device_id_t src, state_sync_prop_id_t propId, const DongleLeds_Update(); } break; + case StateSyncPropertyId_DongleScrollMultipliers: + if (!isLocalUpdate) { + DongleScrollMultipliers = *(scroll_multipliers_t*)data; + } + break; default: printk("Property %i ('%s') has no receive handler. If this is correct, please add a " "separate empty case...\n", @@ -600,6 +608,7 @@ static bool handlePropertyUpdateDongleToRight() { UPDATE_AND_RETURN_IF_DIRTY(StateSyncPropertyId_ResetRightDongleLink); UPDATE_AND_RETURN_IF_DIRTY(StateSyncPropertyId_KeyboardLedsState); + UPDATE_AND_RETURN_IF_DIRTY(StateSyncPropertyId_DongleScrollMultipliers); return true; } @@ -735,6 +744,7 @@ void StateSync_ResetRightDongleLink(bool bidirectional) { if (DEVICE_ID == DeviceId_Uhk_Dongle) { DongleStandby = false; invalidateProperty(StateSyncPropertyId_KeyboardLedsState); + invalidateProperty(StateSyncPropertyId_DongleScrollMultipliers); } } diff --git a/device/src/state_sync.h b/device/src/state_sync.h index e5b087751..e1c8a4bd0 100644 --- a/device/src/state_sync.h +++ b/device/src/state_sync.h @@ -39,6 +39,11 @@ sync_command_action_t actions[KEY_COUNT_PER_UPDATE]; } sync_command_layer_t; + typedef struct { + uint16_t vertical; + uint16_t horizontal; + } scroll_multipliers_t; + typedef struct { uint8_t BacklightingMode; uint8_t KeyBacklightBrightness; @@ -96,6 +101,7 @@ StateSyncPropertyId_Config = 27, StateSyncPropertyId_SwitchTestMode = 28, StateSyncPropertyId_DongleStandby = 29, + StateSyncPropertyId_DongleScrollMultipliers = 30, StateSyncPropertyId_Count, } state_sync_prop_id_t; @@ -129,6 +135,7 @@ extern sync_generic_half_state_t SyncLeftHalfState; extern sync_generic_half_state_t SyncRightHalfState; + extern scroll_multipliers_t DongleScrollMultipliers; // Functions: diff --git a/device/src/usb/mouse_app.cpp b/device/src/usb/mouse_app.cpp index 6ba4a2d0b..a1a0c0f23 100644 --- a/device/src/usb/mouse_app.cpp +++ b/device/src/usb/mouse_app.cpp @@ -2,6 +2,10 @@ #include "app_base.hpp" #include "zephyr/sys/printk.h" +extern "C" { +#include "state_sync.h" +} + mouse_app &mouse_app::usb_handle() { static mouse_app app{}; @@ -35,6 +39,13 @@ void mouse_app::set_report(hid::report::type type, const std::span(data.data()); receive_report(&resolution_buffer_); + + // When running on dongle, update the scroll multiplier state + if (DEVICE_IS_UHK_DONGLE) { + DongleScrollMultipliers.vertical = resolution_buffer_.vertical_scroll_multiplier(); + DongleScrollMultipliers.horizontal = resolution_buffer_.horizontal_scroll_multiplier(); + StateSync_UpdateProperty(StateSyncPropertyId_DongleScrollMultipliers, NULL); + } } void mouse_app::get_report(hid::report::selector select, const std::span &buffer) diff --git a/device/src/usb/usb_compatibility.cpp b/device/src/usb/usb_compatibility.cpp index 32268eebd..986e0659c 100644 --- a/device/src/usb/usb_compatibility.cpp +++ b/device/src/usb/usb_compatibility.cpp @@ -229,10 +229,36 @@ extern "C" void UsbCompatibility_SetKeyboardLedsState(connection_id_t connection extern "C" float VerticalScrollMultiplier(void) { - return mouse_app::usb_handle().resolution_report().vertical_scroll_multiplier(); + switch (Connections_Type(ActiveHostConnectionId)) { + case ConnectionType_UsbHidRight: + case ConnectionType_UsbHidLeft: + return mouse_app::usb_handle().resolution_report().vertical_scroll_multiplier(); +#if DEVICE_IS_UHK80_RIGHT + case ConnectionType_BtHid: + case ConnectionType_NewBtHid: + return mouse_app::ble_handle().resolution_report().vertical_scroll_multiplier(); +#endif + case ConnectionType_NusDongle: + return DongleScrollMultipliers.vertical; + default: + return mouse_app::usb_handle().resolution_report().vertical_scroll_multiplier(); + } } extern "C" float HorizontalScrollMultiplier(void) { - return mouse_app::usb_handle().resolution_report().horizontal_scroll_multiplier(); + switch (Connections_Type(ActiveHostConnectionId)) { + case ConnectionType_UsbHidRight: + case ConnectionType_UsbHidLeft: + return mouse_app::usb_handle().resolution_report().horizontal_scroll_multiplier(); +#if DEVICE_IS_UHK80_RIGHT + case ConnectionType_BtHid: + case ConnectionType_NewBtHid: + return mouse_app::ble_handle().resolution_report().horizontal_scroll_multiplier(); +#endif + case ConnectionType_NusDongle: + return DongleScrollMultipliers.horizontal; + default: + return mouse_app::usb_handle().resolution_report().horizontal_scroll_multiplier(); + } } From 1e2d5176fe48e332b161d8c0feb179d246f17c0f Mon Sep 17 00:00:00 2001 From: Benedek Kupper Date: Tue, 14 Jan 2025 22:45:42 +0100 Subject: [PATCH 4/7] remove extra variable --- right/src/usb_interfaces/usb_interface_mouse.c | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/right/src/usb_interfaces/usb_interface_mouse.c b/right/src/usb_interfaces/usb_interface_mouse.c index aeae3ad05..7f073c888 100644 --- a/right/src/usb_interfaces/usb_interface_mouse.c +++ b/right/src/usb_interfaces/usb_interface_mouse.c @@ -61,16 +61,14 @@ 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; + return usbMouseFeatBuffer[0] & 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; + return usbMouseFeatBuffer[0] & 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) @@ -80,7 +78,7 @@ usb_status_t UsbMouseCallback(class_handle_t handle, uint32_t event, void *param switch (event) { case ((uint32_t)-kUSB_DeviceEventSetConfiguration): - scrollMultipliers = 0; + usbMouseFeatBuffer[0] = 0; error = kStatus_USB_Success; break; case ((uint32_t)-kUSB_DeviceEventSetInterface): @@ -106,7 +104,6 @@ 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] = scrollMultipliers; report->reportBuffer = usbMouseFeatBuffer; report->reportLength = sizeof(usbMouseFeatBuffer); error = kStatus_USB_Success; @@ -119,7 +116,6 @@ 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)) { - scrollMultipliers = usbMouseFeatBuffer[0]; error = kStatus_USB_Success; } else { error = kStatus_USB_InvalidRequest; From ff80fe88ecfb9c508273e9a2d57e7029e551439b Mon Sep 17 00:00:00 2001 From: Benedek Kupper Date: Tue, 14 Jan 2025 22:50:21 +0100 Subject: [PATCH 5/7] restore the workaround from before --- right/src/usb_interfaces/usb_interface_mouse.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/right/src/usb_interfaces/usb_interface_mouse.c b/right/src/usb_interfaces/usb_interface_mouse.c index 7f073c888..efafdc9cf 100644 --- a/right/src/usb_interfaces/usb_interface_mouse.c +++ b/right/src/usb_interfaces/usb_interface_mouse.c @@ -127,6 +127,8 @@ usb_status_t UsbMouseCallback(class_handle_t handle, uint32_t event, void *param 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)) { report->reportBuffer = usbMouseFeatBuffer; + // the only expected written value is this, set it early to workaround the bug + usbMouseFeatBuffer[0] = 0x5; error = kStatus_USB_Success; } else { error = kStatus_USB_AllocFail; From df1654f52025f04223004d2206301592ae0556f8 Mon Sep 17 00:00:00 2001 From: Benedek Kupper Date: Wed, 15 Jan 2025 22:56:10 +0100 Subject: [PATCH 6/7] split the scroll multipliers per direction where possible --- right/src/mouse_controller.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/right/src/mouse_controller.c b/right/src/mouse_controller.c index 6ec175dc1..279f4d9bc 100644 --- a/right/src/mouse_controller.c +++ b/right/src/mouse_controller.c @@ -428,13 +428,15 @@ static void processAxisLocking( caretYModeMultiplier = ks->caretAxis == CaretAxis_Vertical ? 1.0f : axisLockSkew; } - float scrollMultiplier = 1.0f; + float xScrollMultiplier = 1.0f; + float yScrollMultiplier = 1.0f; if (ks->currentNavigationMode == NavigationMode_Scroll) { - scrollMultiplier = VerticalScrollMultiplier(); + xScrollMultiplier = HorizontalScrollMultiplier(); + yScrollMultiplier = VerticalScrollMultiplier(); } - ks->xFractionRemainder += x * speed / speedDivisor * scrollMultiplier * caretXModeMultiplier; - ks->yFractionRemainder += y * speed / speedDivisor * scrollMultiplier * caretYModeMultiplier; + ks->xFractionRemainder += x * speed / speedDivisor * xScrollMultiplier * caretXModeMultiplier; + ks->yFractionRemainder += y * speed / speedDivisor * yScrollMultiplier * caretYModeMultiplier; // Start a new action (new "tick"), unless there is an action in progress. From a427453410e523c129819f153ff820ea82db5e21 Mon Sep 17 00:00:00 2001 From: Benedek Kupper Date: Wed, 15 Jan 2025 23:36:30 +0100 Subject: [PATCH 7/7] revert to shared scroll multiplier on UHK60 --- .../usb_descriptor_mouse_report.h | 50 +++++++++---------- .../src/usb_interfaces/usb_interface_mouse.c | 17 +++++-- 2 files changed, 39 insertions(+), 28 deletions(-) diff --git a/right/src/usb_descriptors/usb_descriptor_mouse_report.h b/right/src/usb_descriptors/usb_descriptor_mouse_report.h index eae03bdac..464e6d697 100644 --- a/right/src/usb_descriptors/usb_descriptor_mouse_report.h +++ b/right/src/usb_descriptors/usb_descriptor_mouse_report.h @@ -61,6 +61,25 @@ 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), @@ -71,37 +90,18 @@ 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 efafdc9cf..22f0c0e76 100644 --- a/right/src/usb_interfaces/usb_interface_mouse.c +++ b/right/src/usb_interfaces/usb_interface_mouse.c @@ -68,7 +68,7 @@ float VerticalScrollMultiplier(void) float HorizontalScrollMultiplier(void) { - return usbMouseFeatBuffer[0] & 0x04 ? USB_MOUSE_REPORT_DESCRIPTOR_MAX_RESOLUTION_MULTIPLIER_PHYSICAL_VALUE : USB_MOUSE_REPORT_DESCRIPTOR_MIN_RESOLUTION_MULTIPLIER_PHYSICAL_VALUE; + return usbMouseFeatBuffer[0] & 0x01 ? 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) @@ -116,6 +116,11 @@ 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. error = kStatus_USB_Success; } else { error = kStatus_USB_InvalidRequest; @@ -126,9 +131,15 @@ 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. report->reportBuffer = usbMouseFeatBuffer; - // the only expected written value is this, set it early to workaround the bug - usbMouseFeatBuffer[0] = 0x5; + usbMouseFeatBuffer[0] = 0x1; error = kStatus_USB_Success; } else { error = kStatus_USB_AllocFail;