Skip to content

Commit

Permalink
Merge pull request #1047 from UltimateHackingKeyboard/high-res-scrolling
Browse files Browse the repository at this point in the history
add high resolution scrolling to UHK-80, switch to standard defined implementation
  • Loading branch information
mondalaci authored Jan 16, 2025
2 parents 783d83e + a427453 commit d1d951b
Show file tree
Hide file tree
Showing 11 changed files with 145 additions and 41 deletions.
10 changes: 10 additions & 0 deletions device/src/state_sync.c
Original file line number Diff line number Diff line change
Expand Up @@ -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) \
Expand Down Expand Up @@ -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),
};


Expand Down Expand Up @@ -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",
Expand Down Expand Up @@ -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;
}
Expand Down Expand Up @@ -735,6 +744,7 @@ void StateSync_ResetRightDongleLink(bool bidirectional) {
if (DEVICE_ID == DeviceId_Uhk_Dongle) {
DongleStandby = false;
invalidateProperty(StateSyncPropertyId_KeyboardLedsState);
invalidateProperty(StateSyncPropertyId_DongleScrollMultipliers);
}
}

Expand Down
7 changes: 7 additions & 0 deletions device/src/state_sync.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -96,6 +101,7 @@
StateSyncPropertyId_Config = 27,
StateSyncPropertyId_SwitchTestMode = 28,
StateSyncPropertyId_DongleStandby = 29,
StateSyncPropertyId_DongleScrollMultipliers = 30,
StateSyncPropertyId_Count,
} state_sync_prop_id_t;

Expand Down Expand Up @@ -129,6 +135,7 @@

extern sync_generic_half_state_t SyncLeftHalfState;
extern sync_generic_half_state_t SyncRightHalfState;
extern scroll_multipliers_t DongleScrollMultipliers;

// Functions:

Expand Down
32 changes: 32 additions & 0 deletions device/src/usb/mouse_app.cpp
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
#include "mouse_app.hpp"
#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{};
Expand All @@ -18,9 +23,36 @@ 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_);

// 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<uint8_t> &buffer)
{
if (select == resolution_buffer_.selector()) {
send_report(&resolution_buffer_);
return;
}
app_base::get_report(select, buffer);
}
45 changes: 22 additions & 23 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 @@ -101,9 +91,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_) {};
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__
38 changes: 37 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 @@ -226,3 +226,39 @@ extern "C" void UsbCompatibility_SetKeyboardLedsState(connection_id_t connection
UsbCompatibility_SetCurrentKeyboardLedsState(state);
#endif
}

extern "C" float VerticalScrollMultiplier(void)
{
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)
{
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();
}
}
17 changes: 12 additions & 5 deletions right/src/mouse_controller.c
Original file line number Diff line number Diff line change
Expand Up @@ -428,8 +428,16 @@ static void processAxisLocking(
caretYModeMultiplier = ks->caretAxis == CaretAxis_Vertical ? 1.0f : axisLockSkew;
}

ks->xFractionRemainder += x * speed / speedDivisor * caretXModeMultiplier;
ks->yFractionRemainder += y * speed / speedDivisor * caretYModeMultiplier;
float xScrollMultiplier = 1.0f;
float yScrollMultiplier = 1.0f;
if (ks->currentNavigationMode == NavigationMode_Scroll) {
xScrollMultiplier = HorizontalScrollMultiplier();
yScrollMultiplier = VerticalScrollMultiplier();
}

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.
if (!caretModeActionIsRunning(ks)) {
Expand Down Expand Up @@ -551,13 +559,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;
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;
Expand Down
23 changes: 14 additions & 9 deletions right/src/usb_interfaces/usb_interface_mouse.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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;
Expand All @@ -65,13 +61,24 @@ usb_status_t UsbMouseAction(void)
return usb_status;
}

float VerticalScrollMultiplier(void)
{
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 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)
{
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):
usbMouseFeatBuffer[0] = 0;
error = kStatus_USB_Success;
break;
case ((uint32_t)-kUSB_DeviceEventSetInterface):
Expand All @@ -97,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] = (uint8_t)(UsbMouseScrollMultiplier != USB_MOUSE_REPORT_DESCRIPTOR_MIN_RESOLUTION_MULTIPLIER_PHYSICAL_VALUE);
report->reportBuffer = usbMouseFeatBuffer;
report->reportLength = sizeof(usbMouseFeatBuffer);
error = kStatus_USB_Success;
Expand All @@ -115,7 +121,6 @@ usb_status_t UsbMouseCallback(class_handle_t handle, uint32_t event, void *param
// 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;
error = kStatus_USB_Success;
} else {
error = kStatus_USB_InvalidRequest;
Expand All @@ -133,8 +138,8 @@ usb_status_t UsbMouseCallback(class_handle_t handle, uint32_t event, void *param
// 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;
usbMouseFeatBuffer[0] = 0x1;
error = kStatus_USB_Success;
} else {
error = kStatus_USB_AllocFail;
Expand Down
Loading

0 comments on commit d1d951b

Please sign in to comment.