diff --git a/docs/features/pointing_device.md b/docs/features/pointing_device.md index a6bf521a184c..c10abe48c4a7 100644 --- a/docs/features/pointing_device.md +++ b/docs/features/pointing_device.md @@ -394,6 +394,7 @@ Ideally, new sensor hardware should be added to `drivers/sensors/` and `quantum/ | Setting | Description | Default | | ---------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------- | ------------- | | `MOUSE_EXTENDED_REPORT` | (Optional) Enables support for extended mouse reports. (-32767 to 32767, instead of just -127 to 127). | _not defined_ | +| `WHEEL_EXTENDED_REPORT` | (Optional) Enables support for extended wheel reports. (-32767 to 32767, instead of just -127 to 127). | _not defined_ | | `POINTING_DEVICE_ROTATION_90` | (Optional) Rotates the X and Y data by 90 degrees. | _not defined_ | | `POINTING_DEVICE_ROTATION_180` | (Optional) Rotates the X and Y data by 180 degrees. | _not defined_ | | `POINTING_DEVICE_ROTATION_270` | (Optional) Rotates the X and Y data by 270 degrees. | _not defined_ | @@ -418,6 +419,30 @@ The `POINTING_DEVICE_CS_PIN`, `POINTING_DEVICE_SDIO_PIN`, and `POINTING_DEVICE_S Any pointing device with a lift/contact status can integrate inertial cursor feature into its driver, controlled by `POINTING_DEVICE_GESTURES_CURSOR_GLIDE_ENABLE`. e.g. PMW3360 can use Lift_Stat from Motion register. Note that `POINTING_DEVICE_MOTION_PIN` cannot be used with this feature; continuous polling of `get_report()` is needed to generate glide reports. ::: +## High Resolution Scrolling + +| Setting | Description | Default | +| ---------------------------------------- | ------------------------------------------------------------------------------------------------------------------------- | ------------- | +| `POINTING_DEVICE_HIRES_SCROLL_ENABLE` | (Optional) Enables high resolution scrolling. | _not defined_ | +| `POINTING_DEVICE_HIRES_SCROLL_MULTIPLIER`| (Optional) Resolution mutiplier value used by high resolution scrolling. Must be between 1 and 127, inclusive. | `120` | +| `POINTING_DEVICE_HIRES_SCROLL_EXPONENT` | (Optional) Resolution exponent value used by high resolution scrolling. Must be between 1 and 127, inclusive. | `0` | + +The `POINTING_DEVICE_HIRES_SCROLL_ENABLE` setting enables smooth and continuous scrolling when using trackballs or high-end encoders as mouse wheels (as opposed to the typical stepped behavior of most mouse wheels). +This works by adding a resolution multiplier to the HID descriptor for mouse wheel reports, causing the host computer to interpret each wheel tick sent by the keyboard as a fraction of a normal wheel tick. +The resolution multiplier is set to `1 / (POINTING_DEVICE_HIRES_SCROLL_MULTIPLIER * (10 ^ POINTING_DEVICE_HIRES_SCROLL_EXPONENT))`, which is `1 / 120` by default. +If even smoother scrolling than provided by this default value is desired, first try using `#define POINTING_DEVICE_HIRES_SCROLL_EXPONENT 1` which will result in a multiplier of `1 / 1200`. + +::: warning +High resolution scrolling usually results in larger and/or more frequent mouse reports. This can result in overflow errors and overloading of the host computer's input buffer. +To deal with these issues, define `WHEEL_EXTENDED_REPORT` and throttle the rate at which mouse reports are sent. +::: + +::: warning +Many programs, especially those that implement their own smoothing for scrolling, don't work well when they receive simultaneous vertical and horizontal wheel inputs (e.g. from high resolution drag-scroll using a trackball). +These programs typically implement their smoothing in a way that assumes the user will only scroll in one axis at a time, resulting in slow or jittery motion when trying to scroll at an angle. +This can be addressed by snapping scrolling to one axis at a time. +::: + ## Split Keyboard Configuration The following configuration options are only available when using `SPLIT_POINTING_ENABLE` see [data sync options](split_keyboard#data-sync-options). The rotation and invert `*_RIGHT` options are only used with `POINTING_DEVICE_COMBINED`. If using `POINTING_DEVICE_LEFT` or `POINTING_DEVICE_RIGHT` use the common configuration above to configure your pointing device. diff --git a/quantum/pointing_device/pointing_device.c b/quantum/pointing_device/pointing_device.c index 74ce9108a9cf..b0fe4dcd0263 100644 --- a/quantum/pointing_device/pointing_device.c +++ b/quantum/pointing_device/pointing_device.c @@ -25,6 +25,10 @@ # include "mousekey.h" #endif +#ifdef POINTING_DEVICE_HIRES_SCROLL_ENABLE +# include "usb_descriptor_common.h" +#endif + #if (defined(POINTING_DEVICE_ROTATION_90) + defined(POINTING_DEVICE_ROTATION_180) + defined(POINTING_DEVICE_ROTATION_270)) > 1 # error More than one rotation selected. This is not supported. #endif @@ -78,6 +82,9 @@ uint16_t pointing_device_get_shared_cpi(void) { static report_mouse_t local_mouse_report = {}; static bool pointing_device_force_send = false; +#ifdef POINTING_DEVICE_HIRES_SCROLL_ENABLE +static uint16_t hires_scroll_resolution; +#endif extern const pointing_device_driver_t pointing_device_driver; @@ -155,6 +162,12 @@ __attribute__((weak)) void pointing_device_init(void) { # endif #endif } +#ifdef POINTING_DEVICE_HIRES_SCROLL_ENABLE + hires_scroll_resolution = POINTING_DEVICE_HIRES_SCROLL_MULTIPLIER; + for (int i = 0; i < POINTING_DEVICE_HIRES_SCROLL_EXPONENT; i++) { + hires_scroll_resolution *= 10; + } +#endif pointing_device_init_kb(); pointing_device_init_user(); @@ -377,28 +390,28 @@ void pointing_device_set_cpi_on_side(bool left, uint16_t cpi) { } /** - * @brief clamps int16_t to int8_t + * @brief clamps int16_t to int8_t, or int32_t to int16_t * - * @param[in] int16_t value - * @return int8_t clamped value + * @param[in] hv_clamp_range_t value + * @return mouse_hv_report_t clamped value */ -static inline int8_t pointing_device_hv_clamp(int16_t value) { - if (value < INT8_MIN) { - return INT8_MIN; - } else if (value > INT8_MAX) { - return INT8_MAX; +static inline mouse_hv_report_t pointing_device_hv_clamp(hv_clamp_range_t value) { + if (value < HV_REPORT_MIN) { + return HV_REPORT_MIN; + } else if (value > HV_REPORT_MAX) { + return HV_REPORT_MAX; } else { return value; } } /** - * @brief clamps int16_t to int8_t + * @brief clamps int16_t to int8_t, or int32_t to int16_t * - * @param[in] clamp_range_t value + * @param[in] xy_clamp_range_t value * @return mouse_xy_report_t clamped value */ -static inline mouse_xy_report_t pointing_device_xy_clamp(clamp_range_t value) { +static inline mouse_xy_report_t pointing_device_xy_clamp(xy_clamp_range_t value) { if (value < XY_REPORT_MIN) { return XY_REPORT_MIN; } else if (value > XY_REPORT_MAX) { @@ -419,10 +432,10 @@ static inline mouse_xy_report_t pointing_device_xy_clamp(clamp_range_t value) { * @return combined report_mouse_t of left_report and right_report */ report_mouse_t pointing_device_combine_reports(report_mouse_t left_report, report_mouse_t right_report) { - left_report.x = pointing_device_xy_clamp((clamp_range_t)left_report.x + right_report.x); - left_report.y = pointing_device_xy_clamp((clamp_range_t)left_report.y + right_report.y); - left_report.h = pointing_device_hv_clamp((int16_t)left_report.h + right_report.h); - left_report.v = pointing_device_hv_clamp((int16_t)left_report.v + right_report.v); + left_report.x = pointing_device_xy_clamp((xy_clamp_range_t)left_report.x + right_report.x); + left_report.y = pointing_device_xy_clamp((xy_clamp_range_t)left_report.y + right_report.y); + left_report.h = pointing_device_hv_clamp((hv_clamp_range_t)left_report.h + right_report.h); + left_report.v = pointing_device_hv_clamp((hv_clamp_range_t)left_report.v + right_report.v); left_report.buttons |= right_report.buttons; return left_report; } @@ -502,3 +515,9 @@ __attribute__((weak)) void pointing_device_keycode_handler(uint16_t keycode, boo pointing_device_send(); } } + +#ifdef POINTING_DEVICE_HIRES_SCROLL_ENABLE +uint16_t pointing_device_get_hires_scroll_resolution(void) { + return hires_scroll_resolution; +} +#endif \ No newline at end of file diff --git a/quantum/pointing_device/pointing_device.h b/quantum/pointing_device/pointing_device.h index 1cd4b0b5e60d..e620bf8acd5b 100644 --- a/quantum/pointing_device/pointing_device.h +++ b/quantum/pointing_device/pointing_device.h @@ -95,11 +95,21 @@ typedef enum { #ifdef MOUSE_EXTENDED_REPORT # define XY_REPORT_MIN INT16_MIN # define XY_REPORT_MAX INT16_MAX -typedef int32_t clamp_range_t; +typedef int32_t xy_clamp_range_t; #else # define XY_REPORT_MIN INT8_MIN # define XY_REPORT_MAX INT8_MAX -typedef int16_t clamp_range_t; +typedef int16_t xy_clamp_range_t; +#endif + +#ifdef WHEEL_EXTENDED_REPORT +# define HV_REPORT_MIN INT16_MIN +# define HV_REPORT_MAX INT16_MAX +typedef int32_t hv_clamp_range_t; +#else +# define HV_REPORT_MIN INT8_MIN +# define HV_REPORT_MAX INT8_MAX +typedef int16_t hv_clamp_range_t; #endif void pointing_device_init(void); @@ -118,6 +128,10 @@ uint8_t pointing_device_handle_buttons(uint8_t buttons, bool pressed, poi report_mouse_t pointing_device_adjust_by_defines(report_mouse_t mouse_report); void pointing_device_keycode_handler(uint16_t keycode, bool pressed); +#ifdef POINTING_DEVICE_HIRES_SCROLL_ENABLE +uint16_t pointing_device_get_hires_scroll_resolution(void); +#endif + #if defined(SPLIT_POINTING_ENABLE) void pointing_device_set_shared_report(report_mouse_t report); uint16_t pointing_device_get_shared_cpi(void); diff --git a/quantum/pointing_device/pointing_device_drivers.c b/quantum/pointing_device/pointing_device_drivers.c index bf131c6eda51..6cbe427401ec 100644 --- a/quantum/pointing_device/pointing_device_drivers.c +++ b/quantum/pointing_device/pointing_device_drivers.c @@ -391,7 +391,7 @@ const pointing_device_driver_t pointing_device_driver = { }; #elif defined(POINTING_DEVICE_DRIVER_pimoroni_trackball) -mouse_xy_report_t pimoroni_trackball_adapt_values(clamp_range_t* offset) { +mouse_xy_report_t pimoroni_trackball_adapt_values(xy_clamp_range_t* offset) { if (*offset > XY_REPORT_MAX) { *offset -= XY_REPORT_MAX; return (mouse_xy_report_t)XY_REPORT_MAX; @@ -406,10 +406,10 @@ mouse_xy_report_t pimoroni_trackball_adapt_values(clamp_range_t* offset) { } report_mouse_t pimoroni_trackball_get_report(report_mouse_t mouse_report) { - static uint16_t debounce = 0; - static uint8_t error_count = 0; - pimoroni_data_t pimoroni_data = {0}; - static clamp_range_t x_offset = 0, y_offset = 0; + static uint16_t debounce = 0; + static uint8_t error_count = 0; + pimoroni_data_t pimoroni_data = {0}; + static xy_clamp_range_t x_offset = 0, y_offset = 0; if (error_count < PIMORONI_TRACKBALL_ERROR_COUNT) { i2c_status_t status = read_pimoroni_trackball(&pimoroni_data); diff --git a/tmk_core/protocol/chibios/usb_main.c b/tmk_core/protocol/chibios/usb_main.c index 2024a3bc7f24..2c0ef952e1ff 100644 --- a/tmk_core/protocol/chibios/usb_main.c +++ b/tmk_core/protocol/chibios/usb_main.c @@ -57,6 +57,9 @@ extern usb_endpoint_out_t usb_endpoints_out[USB_ENDPOINT_OUT_COUNT]; uint8_t _Alignas(2) keyboard_idle = 0; uint8_t _Alignas(2) keyboard_protocol = 1; uint8_t keyboard_led_state = 0; +#ifdef POINTING_DEVICE_HIRES_SCROLL_ENABLE +uint8_t hires_scroll_state = 0; +#endif static bool __attribute__((__unused__)) send_report_buffered(usb_endpoint_in_lut_t endpoint, void *report, size_t size); static void __attribute__((__unused__)) flush_report_buffered(usb_endpoint_in_lut_t endpoint, bool padded); @@ -84,7 +87,7 @@ static const USBDescriptor *usb_get_descriptor_cb(USBDriver *usbp, uint8_t dtype static USBDescriptor descriptor; descriptor.ud_string = NULL; - descriptor.ud_size = get_usb_descriptor(setup->wValue.word, setup->wIndex, setup->wLength, (const void **const) & descriptor.ud_string); + descriptor.ud_size = get_usb_descriptor(setup->wValue.word, setup->wIndex, setup->wLength, (const void **const)&descriptor.ud_string); if (descriptor.ud_string == NULL) { return NULL; @@ -244,18 +247,32 @@ static void usb_event_cb(USBDriver *usbp, usbevent_t event) { static uint8_t _Alignas(4) set_report_buf[2]; -static void set_led_transfer_cb(USBDriver *usbp) { - usb_control_request_t *setup = (usb_control_request_t *)usbp->setup; - - if (setup->wLength == 2) { - uint8_t report_id = set_report_buf[0]; - if ((report_id == REPORT_ID_KEYBOARD) || (report_id == REPORT_ID_NKRO)) { +#if !defined(KEYBOARD_SHARED_EP) +static void set_transfer_cb_keyboard(USBDriver *usbp) { + keyboard_led_state = set_report_buf[0]; +} +#endif +#if defined(MOUSE_ENABLE) && !defined(MOUSE_SHARED_EP) && defined(POINTING_DEVICE_HIRES_SCROLL_ENABLE) +static void set_transfer_cb_mouse(USBDriver *usbp) { + hires_scroll_state = set_report_buf[0]; +} +#endif +#if defined(SHARED_EP_ENABLE) +static void set_transfer_cb_shared(USBDriver *usbp) { + uint8_t report_id = set_report_buf[0]; + switch (report_id) { + case REPORT_ID_KEYBOARD: + case REPORT_ID_NKRO: keyboard_led_state = set_report_buf[1]; - } - } else { - keyboard_led_state = set_report_buf[0]; + return; +# if defined(POINTING_DEVICE_HIRES_SCROLL_ENABLE) + case REPORT_ID_MOUSE: + hires_scroll_state = set_report_buf[1]; + return; +# endif } } +#endif static bool usb_requests_hook_cb(USBDriver *usbp) { usb_control_request_t *setup = (usb_control_request_t *)usbp->setup; @@ -282,12 +299,21 @@ static bool usb_requests_hook_cb(USBDriver *usbp) { switch (setup->bRequest) { case HID_REQ_SetReport: switch (setup->wIndex) { +#if !defined(KEYBOARD_SHARED_EP) case KEYBOARD_INTERFACE: -#if defined(SHARED_EP_ENABLE) && !defined(KEYBOARD_SHARED_EP) - case SHARED_INTERFACE: + usbSetupTransfer(usbp, set_report_buf, sizeof(set_report_buf), set_transfer_cb_keyboard); + return true; +#endif +#if defined(MOUSE_ENABLE) && !defined(MOUSE_SHARED_EP) && defined(POINTING_DEVICE_HIRES_SCROLL_ENABLE) + case MOUSE_INTERFACE: + usbSetupTransfer(usbp, set_report_buf, sizeof(set_report_buf), set_transfer_cb_mouse); + return true; #endif - usbSetupTransfer(usbp, set_report_buf, sizeof(set_report_buf), set_led_transfer_cb); +#if defined(SHARED_EP_ENABLE) + case SHARED_INTERFACE: + usbSetupTransfer(usbp, set_report_buf, sizeof(set_report_buf), set_transfer_cb_shared); return true; +#endif } break; case HID_REQ_SetProtocol: @@ -482,6 +508,12 @@ void send_mouse(report_mouse_t *report) { #endif } +#ifdef POINTING_DEVICE_HIRES_SCROLL_ENABLE +bool is_hires_scroll_on(void) { + return hires_scroll_state > 0; +} +#endif + /* --------------------------------------------------------- * Extrakey functions * --------------------------------------------------------- diff --git a/tmk_core/protocol/host.c b/tmk_core/protocol/host.c index 732fbdc37d4d..e1987563313b 100644 --- a/tmk_core/protocol/host.c +++ b/tmk_core/protocol/host.c @@ -55,7 +55,7 @@ host_driver_t *host_get_driver(void) { #ifdef SPLIT_KEYBOARD uint8_t split_led_state = 0; void set_split_host_keyboard_leds(uint8_t led_state) { - split_led_state = led_state; + split_led_state = led_state; } #endif @@ -129,6 +129,14 @@ void host_mouse_send(report_mouse_t *report) { (*driver->send_mouse)(report); } +__attribute__((weak)) bool is_hires_scroll_on(void) { +#ifdef POINTING_DEVICE_HIRES_SCROLL_ENABLE + return true; +#else + return false; +#endif +} + void host_system_send(uint16_t usage) { if (usage == last_system_usage) return; last_system_usage = usage; diff --git a/tmk_core/protocol/host_driver.h b/tmk_core/protocol/host_driver.h index 8aa38b6dee2c..b1cdb2ad4f86 100644 --- a/tmk_core/protocol/host_driver.h +++ b/tmk_core/protocol/host_driver.h @@ -31,6 +31,7 @@ typedef struct { void (*send_extra)(report_extra_t *); } host_driver_t; +bool is_hires_scroll_on(void); void send_joystick(report_joystick_t *report); void send_digitizer(report_digitizer_t *report); void send_programmable_button(report_programmable_button_t *report); diff --git a/tmk_core/protocol/lufa/lufa.c b/tmk_core/protocol/lufa/lufa.c index b0c9758d2fd5..1b3d650aa073 100644 --- a/tmk_core/protocol/lufa/lufa.c +++ b/tmk_core/protocol/lufa/lufa.c @@ -76,6 +76,9 @@ uint8_t keyboard_idle = 0; /* 0: Boot Protocol, 1: Report Protocol(default) */ uint8_t keyboard_protocol = 1; static uint8_t keyboard_led_state = 0; +#ifdef POINTING_DEVICE_HIRES_SCROLL_ENABLE +static uint8_t hires_scroll_state = 0; +#endif static report_keyboard_t keyboard_report_sent; @@ -439,29 +442,50 @@ void EVENT_USB_Device_ControlRequest(void) { if (USB_ControlRequest.bmRequestType == (REQDIR_HOSTTODEVICE | REQTYPE_CLASS | REQREC_INTERFACE)) { // Interface switch (USB_ControlRequest.wIndex) { +#if !defined(KEYBOARD_SHARED_EP) case KEYBOARD_INTERFACE: -#if defined(SHARED_EP_ENABLE) && !defined(KEYBOARD_SHARED_EP) - case SHARED_INTERFACE: + Endpoint_ClearSETUP(); + while (!(Endpoint_IsOUTReceived())) { + if (USB_DeviceState == DEVICE_STATE_Unattached) return; + } + keyboard_led_state = Endpoint_Read_8(); + Endpoint_ClearOUT(); + Endpoint_ClearStatusStage(); + break; #endif +#if defined(MOUSE_ENABLE) && !defined(MOUSE_SHARED_EP) && defined(POINTING_DEVICE_HIRES_SCROLL_ENABLE) + case MOUSE_INTERFACE: Endpoint_ClearSETUP(); - while (!(Endpoint_IsOUTReceived())) { if (USB_DeviceState == DEVICE_STATE_Unattached) return; } - - if (Endpoint_BytesInEndpoint() == 2) { - uint8_t report_id = Endpoint_Read_8(); - - if (report_id == REPORT_ID_KEYBOARD || report_id == REPORT_ID_NKRO) { + hires_scroll_state = Endpoint_Read_8(); + Endpoint_ClearOUT(); + Endpoint_ClearStatusStage(); + break; +#endif +#if defined(SHARED_EP_ENABLE) + case SHARED_INTERFACE: + Endpoint_ClearSETUP(); + while (!(Endpoint_IsOUTReceived())) { + if (USB_DeviceState == DEVICE_STATE_Unattached) return; + } + uint8_t report_id = Endpoint_Read_8(); + switch (report_id) { + case REPORT_ID_KEYBOARD: + case REPORT_ID_NKRO: keyboard_led_state = Endpoint_Read_8(); - } - } else { - keyboard_led_state = Endpoint_Read_8(); + break; +# if defined(POINTING_DEVICE_HIRES_SCROLL_ENABLE) + case REPORT_ID_MOUSE: + hires_scroll_state = Endpoint_Read_8(); + break; +# endif } - Endpoint_ClearOUT(); Endpoint_ClearStatusStage(); break; +#endif } } @@ -575,6 +599,12 @@ static void send_extra(report_extra_t *report) { #endif } +#ifdef POINTING_DEVICE_HIRES_SCROLL_ENABLE +bool is_hires_scroll_on(void) { + return hires_scroll_state > 0; +} +#endif + void send_joystick(report_joystick_t *report) { #ifdef JOYSTICK_ENABLE send_report(JOYSTICK_IN_EPNUM, report, sizeof(report_joystick_t)); diff --git a/tmk_core/protocol/report.h b/tmk_core/protocol/report.h index 0e4f6e9defa5..37c8ea48f1b6 100644 --- a/tmk_core/protocol/report.h +++ b/tmk_core/protocol/report.h @@ -199,6 +199,12 @@ typedef int16_t mouse_xy_report_t; typedef int8_t mouse_xy_report_t; #endif +#ifdef WHEEL_EXTENDED_REPORT +typedef int16_t mouse_hv_report_t; +#else +typedef int8_t mouse_hv_report_t; +#endif + typedef struct { #ifdef MOUSE_SHARED_EP uint8_t report_id; @@ -210,8 +216,8 @@ typedef struct { #endif mouse_xy_report_t x; mouse_xy_report_t y; - int8_t v; - int8_t h; + mouse_hv_report_t v; + mouse_hv_report_t h; } PACKED report_mouse_t; typedef struct { diff --git a/tmk_core/protocol/usb_descriptor.c b/tmk_core/protocol/usb_descriptor.c index 7efd085ea32e..3ed786263ff9 100644 --- a/tmk_core/protocol/usb_descriptor.c +++ b/tmk_core/protocol/usb_descriptor.c @@ -155,21 +155,55 @@ const USB_Descriptor_HIDReport_Datatype_t PROGMEM SharedReport[] = { # endif HID_RI_INPUT(8, HID_IOF_DATA | HID_IOF_VARIABLE | HID_IOF_RELATIVE), - // Vertical wheel (1 byte) - HID_RI_USAGE(8, 0x38), // Wheel - HID_RI_LOGICAL_MINIMUM(8, -127), - HID_RI_LOGICAL_MAXIMUM(8, 127), - HID_RI_REPORT_COUNT(8, 0x01), - HID_RI_REPORT_SIZE(8, 0x08), - HID_RI_INPUT(8, HID_IOF_DATA | HID_IOF_VARIABLE | HID_IOF_RELATIVE), - // Horizontal wheel (1 byte) - HID_RI_USAGE_PAGE(8, 0x0C), // Consumer - HID_RI_USAGE(16, 0x0238), // AC Pan - HID_RI_LOGICAL_MINIMUM(8, -127), - HID_RI_LOGICAL_MAXIMUM(8, 127), - HID_RI_REPORT_COUNT(8, 0x01), - HID_RI_REPORT_SIZE(8, 0x08), - HID_RI_INPUT(8, HID_IOF_DATA | HID_IOF_VARIABLE | HID_IOF_RELATIVE), +# ifdef POINTING_DEVICE_HIRES_SCROLL_ENABLE + HID_RI_COLLECTION(8, 0x02), + // Feature report and padding (1 byte) + HID_RI_USAGE(8, 0x48), // Resolution Multiplier + HID_RI_REPORT_COUNT(8, 0x01), + HID_RI_REPORT_SIZE(8, 0x02), + HID_RI_LOGICAL_MINIMUM(8, 0x00), + HID_RI_LOGICAL_MAXIMUM(8, 0x01), + HID_RI_PHYSICAL_MINIMUM(8, 1), + HID_RI_PHYSICAL_MAXIMUM(8, POINTING_DEVICE_HIRES_SCROLL_MULTIPLIER), + HID_RI_UNIT_EXPONENT(8, POINTING_DEVICE_HIRES_SCROLL_EXPONENT), + HID_RI_FEATURE(8, HID_IOF_DATA | HID_IOF_VARIABLE | HID_IOF_ABSOLUTE), + HID_RI_PHYSICAL_MINIMUM(8, 0x00), + HID_RI_PHYSICAL_MAXIMUM(8, 0x00), + HID_RI_REPORT_SIZE(8, 0x06), + HID_RI_FEATURE(8, HID_IOF_CONSTANT), +# endif + // Vertical wheel (1 or 2 bytes) + HID_RI_USAGE(8, 0x38), // Wheel +# ifndef WHEEL_EXTENDED_REPORT + HID_RI_LOGICAL_MINIMUM(8, -127), + HID_RI_LOGICAL_MAXIMUM(8, 127), + HID_RI_REPORT_COUNT(8, 0x01), + HID_RI_REPORT_SIZE(8, 0x08), +# else + HID_RI_LOGICAL_MINIMUM(16, -32767), + HID_RI_LOGICAL_MAXIMUM(16, 32767), + HID_RI_REPORT_COUNT(8, 0x01), + HID_RI_REPORT_SIZE(8, 0x10), +# endif + HID_RI_INPUT(8, HID_IOF_DATA | HID_IOF_VARIABLE | HID_IOF_RELATIVE), + // Horizontal wheel (1 or 2 bytes) + HID_RI_USAGE_PAGE(8, 0x0C),// Consumer + HID_RI_USAGE(16, 0x0238), // AC Pan +# ifndef WHEEL_EXTENDED_REPORT + HID_RI_LOGICAL_MINIMUM(8, -127), + HID_RI_LOGICAL_MAXIMUM(8, 127), + HID_RI_REPORT_COUNT(8, 0x01), + HID_RI_REPORT_SIZE(8, 0x08), +# else + HID_RI_LOGICAL_MINIMUM(16, -32767), + HID_RI_LOGICAL_MAXIMUM(16, 32767), + HID_RI_REPORT_COUNT(8, 0x01), + HID_RI_REPORT_SIZE(8, 0x10), +# endif + HID_RI_INPUT(8, HID_IOF_DATA | HID_IOF_VARIABLE | HID_IOF_RELATIVE), +# ifdef POINTING_DEVICE_HIRES_SCROLL_ENABLE + HID_RI_END_COLLECTION(0), +# endif HID_RI_END_COLLECTION(0), HID_RI_END_COLLECTION(0), # ifndef MOUSE_SHARED_EP diff --git a/tmk_core/protocol/usb_descriptor_common.h b/tmk_core/protocol/usb_descriptor_common.h index 909c230a9921..9fa9b136a5cd 100644 --- a/tmk_core/protocol/usb_descriptor_common.h +++ b/tmk_core/protocol/usb_descriptor_common.h @@ -32,3 +32,23 @@ #ifndef RAW_USAGE_ID # define RAW_USAGE_ID 0x61 #endif + +///////////////////// +// Hires Scroll Defaults + +#ifdef POINTING_DEVICE_HIRES_SCROLL_ENABLE +# ifdef POINTING_DEVICE_HIRES_SCROLL_MULTIPLIER +# if POINTING_DEVICE_HIRES_SCROLL_MULTIPLIER > 127 || POINTING_DEVICE_HIRES_SCROLL_MULTIPLIER < 1 +# error "POINTING_DEVICE_HIRES_SCROLL_MULTIPLIER must be between 1 and 127, inclusive!" +# endif +# else +# define POINTING_DEVICE_HIRES_SCROLL_MULTIPLIER 120 +# endif +# ifdef POINTING_DEVICE_HIRES_SCROLL_EXPONENT +# if POINTING_DEVICE_HIRES_SCROLL_EXPONENT > 127 || POINTING_DEVICE_HIRES_SCROLL_EXPONENT < 1 +# error "POINTING_DEVICE_HIRES_SCROLL_EXPONENT must be between 1 and 127, inclusive!" +# endif +# else +# define POINTING_DEVICE_HIRES_SCROLL_EXPONENT 0 +# endif +#endif \ No newline at end of file diff --git a/tmk_core/protocol/vusb/vusb.c b/tmk_core/protocol/vusb/vusb.c index c8ab49425366..eace1ec3d284 100644 --- a/tmk_core/protocol/vusb/vusb.c +++ b/tmk_core/protocol/vusb/vusb.c @@ -86,8 +86,11 @@ _Static_assert(TOTAL_INTERFACES <= MAX_INTERFACES, "There are not enough availab #endif static uint8_t keyboard_led_state = 0; -uint8_t keyboard_idle = 0; -uint8_t keyboard_protocol = 1; +#ifdef POINTING_DEVICE_HIRES_SCROLL_ENABLE +static uint8_t hires_scroll_state = 0; +#endif +uint8_t keyboard_idle = 0; +uint8_t keyboard_protocol = 1; static report_keyboard_t keyboard_report_sent; @@ -258,6 +261,12 @@ static void send_mouse(report_mouse_t *report) { #endif } +#ifdef POINTING_DEVICE_HIRES_SCROLL_ENABLE +bool is_hires_scroll_on(void) { + return hires_scroll_state > 0; +} +#endif + static void send_extra(report_extra_t *report) { #ifdef EXTRAKEY_ENABLE send_report(SHARED_IN_EPNUM, report, sizeof(report_extra_t)); @@ -287,7 +296,11 @@ void send_programmable_button(report_programmable_button_t *report) { *------------------------------------------------------------------*/ static struct { uint16_t len; +#ifdef POINTING_DEVICE_HIRES_SCROLL_ENABLE + enum { NONE, SET_LED, SET_HIRES_SCROLL } kind; +#else enum { NONE, SET_LED } kind; +#endif } last_req; usbMsgLen_t usbFunctionSetup(uchar data[8]) { @@ -312,12 +325,45 @@ usbMsgLen_t usbFunctionSetup(uchar data[8]) { return 1; case USBRQ_HID_SET_REPORT: dprint("SET_REPORT:"); - // Report Type: 0x02(Out)/ReportID: 0x00(none) && Interface: 0(keyboard) +#ifdef POINTING_DEVICE_HIRES_SCROLL_ENABLE + // note: when using vusb, the mouse always uses the shared endpoint, so we don't check for MOUSE_INTERFACE + switch (rq->wIndex.word) { +# ifndef KEYBOARD_SHARED_EP + case KEYBOARD_INTERFACE: + // Report Type: 0x02(Out) / ReportID: none + if (rq->wValue.word == 0x0200) { + dprint("SET_LED:"); + last_req.kind = SET_LED; + last_req.len = rq->wLength.word; + } + break; +# endif +# ifdef SHARED_EP_ENABLE + case SHARED_INTERFACE: + switch (rq->wValue.word) { + // 0x02XX indicates output from computer + case 0x0200 + REPORT_ID_KEYBOARD: + case 0x0200 + REPORT_ID_NKRO: + dprint("SET_LED:"); + last_req.kind = SET_LED; + last_req.len = rq->wLength.word; + break; + case 0x0200 + REPORT_ID_MOUSE: + dprint("SET_HIRES_SCROLL:"); + last_req.kind = SET_HIRES_SCROLL; + last_req.len = rq->wLength.word; + break; + } + break; +# endif + } +#else if (rq->wValue.word == 0x0200 && rq->wIndex.word == KEYBOARD_INTERFACE) { dprint("SET_LED:"); last_req.kind = SET_LED; - last_req.len = rq->wLength.word; + last_req.len = rq->wLength.word; } +#endif return USB_NO_MSG; // to get data in usbFunctionWrite case USBRQ_HID_SET_IDLE: keyboard_idle = (rq->wValue.word & 0xFF00) >> 8; @@ -352,6 +398,14 @@ uchar usbFunctionWrite(uchar *data, uchar len) { last_req.len = 0; return 1; break; +#ifdef POINTING_DEVICE_HIRES_SCROLL_ENABLE + case SET_HIRES_SCROLL: + dprintf("SET_HIRES_SCROLL: %02X\n", data[0]); + hires_scroll_state = data[0]; + last_req.len = 0; + return 1; + break; +#endif case NONE: default: return -1; @@ -524,23 +578,59 @@ const PROGMEM uchar shared_hid_report[] = { # endif 0x81, 0x06, // Input (Data, Variable, Relative) - // Vertical wheel (1 byte) +# ifdef POINTING_DEVICE_HIRES_SCROLL_ENABLE + // Feature report and padding (1 byte) + 0xA1, 0x02, // Collection (Logical) + 0x09, 0x48, // Usage (Resolution Multiplier) + 0x95, 0x01, // Report Count (1) + 0x75, 0x02, // Report Size (2) + 0x15, 0x00, // Logical Minimum (0) + 0x25, 0x01, // Logical Maximum (1) + 0x35, 0x01, // Physical Minimum (1) + 0x45, POINTING_DEVICE_HIRES_SCROLL_MULTIPLIER, // Physical Maximum (POINTING_DEVICE_HIRES_SCROLL_MULTIPLIER) + 0x55, POINTING_DEVICE_HIRES_SCROLL_EXPONENT, // Unit Exponent (POINTING_DEVICE_HIRES_SCROLL_EXPONENT) + 0xB1, 0x02, // Feature (Data, Variable, Absolute) + 0x35, 0x00, // Physical Minimum (0) + 0x45, 0x00, // Physical Maximum (0) + 0x75, 0x06, // Report Size (6) + 0xB1, 0x03, // Feature (Constant) +# endif + + // Vertical wheel (1 or 2 bytes) 0x09, 0x38, // Usage (Wheel) +# ifndef WHEEL_EXTENDED_REPORT 0x15, 0x81, // Logical Minimum (-127) 0x25, 0x7F, // Logical Maximum (127) 0x95, 0x01, // Report Count (1) 0x75, 0x08, // Report Size (8) +# else + 0x16, 0x01, 0x80, // Logical Minimum (-32767) + 0x26, 0xFF, 0x7F, // Logical Maximum (32767) + 0x95, 0x01, // Report Count (1) + 0x75, 0x10, // Report Size (16) +# endif 0x81, 0x06, // Input (Data, Variable, Relative) - // Horizontal wheel (1 byte) + + // Horizontal wheel (1 or 2 bytes) 0x05, 0x0C, // Usage Page (Consumer) 0x0A, 0x38, 0x02, // Usage (AC Pan) - 0x15, 0x81, // Logical Minimum (-127) - 0x25, 0x7F, // Logical Maximum (127) +# ifndef WHEEL_EXTENDED_REPORT + 0x15, 0x81, // Logical Minimum (-127) + 0x25, 0x7F, // Logical Maximum (127) + 0x95, 0x01, // Report Count (1) + 0x75, 0x08, // Report Size (8) +# else + 0x16, 0x01, 0x80, // Logical Minimum (-32767) + 0x26, 0xFF, 0x7F, // Logical Maximum (32767) 0x95, 0x01, // Report Count (1) - 0x75, 0x08, // Report Size (8) - 0x81, 0x06, // Input (Data, Variable, Relative) - 0xC0, // End Collection - 0xC0, // End Collection + 0x75, 0x10, // Report Size (16) +# endif + 0x81, 0x06, // Input (Data, Variable, Relative) +# ifdef POINTING_DEVICE_HIRES_SCROLL_ENABLE + 0xC0, // End Collection +# endif + 0xC0, // End Collection + 0xC0, // End Collection #endif #ifdef EXTRAKEY_ENABLE