Skip to content

Commit

Permalink
add high resolution scrolling to UHK-80, switch to standard defined i…
Browse files Browse the repository at this point in the history
…mplementation
  • Loading branch information
benedekkupper committed Dec 19, 2024
1 parent 7337f9a commit 6ccc9c6
Show file tree
Hide file tree
Showing 10 changed files with 110 additions and 74 deletions.
21 changes: 21 additions & 0 deletions device/src/usb/mouse_app.cpp
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
#include "mouse_app.hpp"
#include "app_base.hpp"
#include "zephyr/sys/printk.h"

mouse_app &mouse_app::handle()
Expand All @@ -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<const uint8_t> &data)
{
if (hid::report::selector(type, data.front()) != resolution_buffer_.selector()) {
return;
}
resolution_buffer_ = *reinterpret_cast<const decltype(resolution_buffer_) *>(data.data());
receive_report(&resolution_buffer_);
}

void mouse_app::get_report(hid::report::selector select, const std::span<uint8_t> &buffer)
{
if (select == resolution_buffer_.selector()) {
send_report(&resolution_buffer_);
return;
}
app_base::get_report(select, buffer);
}
47 changes: 23 additions & 24 deletions device/src/usb/mouse_app.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,15 @@ enum class mouse_button {
_8
};

template <int LIMIT>
using limit_fitted_int =
std::conditional_t<(LIMIT > std::numeric_limits<int8_t>::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()
Expand All @@ -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<int8_t>::max() ? 2 : 1)>(-AXIS_LIMIT, AXIS_LIMIT),
report_count(2),
report_size(16),
report_size(AXIS_LIMIT > std::numeric_limits<int8_t>::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<WHEEL_LIMIT, MAX_SCROLL_RESOLUTION>()
)
)
);
Expand All @@ -79,10 +69,10 @@ class mouse_app : public app_base {
struct mouse_report_base : public hid::report::base<hid::report::type::INPUT, REPORT_ID> {
hid::report_bitset<hid::page::button, hid::page::button(1), mouse_app::LAST_BUTTON>
buttons{};
hid::le_int16_t x{};
hid::le_int16_t y{};
int8_t wheel_y{};
int8_t wheel_x{};
limit_fitted_int<AXIS_LIMIT> x{};
limit_fitted_int<AXIS_LIMIT> y{};
limit_fitted_int<WHEEL_LIMIT> wheel_y{};
limit_fitted_int<WHEEL_LIMIT> wheel_x{};

constexpr mouse_report_base() = default;

Expand All @@ -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<const uint8_t> &data) override;
void get_report(hid::report::selector select, const std::span<uint8_t> &buffer) override;

using mouse_report = mouse_report_base<report_ids::IN_MOUSE>;
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<MAX_SCROLL_RESOLUTION,
report_ids::FEATURE_MOUSE>;
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<>;
Expand Down
2 changes: 2 additions & 0 deletions device/src/usb/report_ids.h
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ enum report_ids {
// OUT
OUT_KEYBOARD_LEDS = 1,
OUT_COMMAND = 4,
// FEATURE
FEATURE_MOUSE = 3,
};

#endif // __REPORT_IDS__
12 changes: 11 additions & 1 deletion device/src/usb/usb_compatibility.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -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();
}
5 changes: 2 additions & 3 deletions right/src/mouse_controller.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
7 changes: 6 additions & 1 deletion right/src/mouse_keys.c
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
#include "config_manager.h"
#include "event_scheduler.h"
#include <string.h>
#include <assert.h>

static uint32_t mouseUsbReportUpdateTime = 0;
static uint32_t mouseElapsedTime;
Expand Down Expand Up @@ -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;
int16_t scrollMultiplier = 1;
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;
Expand Down
50 changes: 25 additions & 25 deletions right/src/usb_descriptors/usb_descriptor_mouse_report.h
Original file line number Diff line number Diff line change
Expand Up @@ -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),
Expand All @@ -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)
Expand Down
35 changes: 17 additions & 18 deletions right/src/usb_interfaces/usb_interface_mouse.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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;
Expand All @@ -60,13 +59,26 @@ 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;
usb_status_t error = kStatus_USB_InvalidRequest;

switch (event) {
case ((uint32_t)-kUSB_DeviceEventSetConfiguration):
scrollMultipliers = 0;
error = kStatus_USB_Success;
break;
case ((uint32_t)-kUSB_DeviceEventSetInterface):
Expand All @@ -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;
Expand All @@ -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;
Expand All @@ -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 {
Expand Down
3 changes: 2 additions & 1 deletion right/src/usb_interfaces/usb_interface_mouse.h
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,6 @@

// Variables:

extern int16_t UsbMouseScrollMultiplier;
extern uint32_t UsbMouseActionCounter;
extern usb_mouse_report_t* ActiveUsbMouseReport;

Expand All @@ -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();
Expand Down
2 changes: 1 addition & 1 deletion west.yml
Original file line number Diff line number Diff line change
Expand Up @@ -36,5 +36,5 @@ manifest:

- name: c2usb
remote: IntergatedCircuits
revision: da4ad86f1cd3a4f8b42aa2d9acdd43e384341ce4
revision: 5af7af626bd82c3937ca16395d6777259effa3fc
clone-depth: 1

0 comments on commit 6ccc9c6

Please sign in to comment.