Browse Source

High resolution scrolling (without feature report parsing) (#24423)

* hires scrolling without feature report parsing

* fix valid range for exponent

* fix incorrect minimum exponent value documentation
eynsai 1 year ago
parent
commit
7a2cd0fa96

+ 26 - 0
docs/features/pointing_device.md

@@ -419,6 +419,32 @@ 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 0 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`.
+
+The function `pointing_device_get_hires_scroll_resolution()` can be called to get the pre-computed resolution multiplier value as a `uint16_t`.
+
+::: 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.

+ 19 - 0
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
 
 #define POINTING_DEVICE_DRIVER_CONCAT(name) name##_pointing_device_driver
 #define POINTING_DEVICE_DRIVER(name) POINTING_DEVICE_DRIVER_CONCAT(name)
@@ -176,6 +183,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();
@@ -523,3 +536,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

+ 4 - 0
quantum/pointing_device/pointing_device.h

@@ -122,6 +122,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);

+ 24 - 0
tmk_core/protocol/usb_descriptor.c

@@ -165,6 +165,24 @@ const USB_Descriptor_HIDReport_Datatype_t PROGMEM SharedReport[] = {
 #    endif
             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
@@ -179,6 +197,7 @@ const USB_Descriptor_HIDReport_Datatype_t PROGMEM SharedReport[] = {
             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
@@ -194,6 +213,11 @@ const USB_Descriptor_HIDReport_Datatype_t PROGMEM SharedReport[] = {
             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

+ 20 - 0
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 < 0
+#            error "POINTING_DEVICE_HIRES_SCROLL_EXPONENT must be between 0 and 127, inclusive!"
+#        endif
+#    else
+#        define POINTING_DEVICE_HIRES_SCROLL_EXPONENT 0
+#    endif
+#endif

+ 26 - 2
tmk_core/protocol/vusb/vusb.c

@@ -520,6 +520,24 @@ const PROGMEM uchar shared_hid_report[] = {
 #    endif
     0x81, 0x06, //     Input (Data, Variable, Relative)
 
+#    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
@@ -534,6 +552,7 @@ const PROGMEM uchar shared_hid_report[] = {
     0x75, 0x10,       //     Report Size (16)
 #    endif
     0x81, 0x06, //     Input (Data, Variable, Relative)
+
     // Horizontal wheel (1 or 2 bytes)
     0x05, 0x0C,       //     Usage Page (Consumer)
     0x0A, 0x38, 0x02, //     Usage (AC Pan)
@@ -549,8 +568,13 @@ const PROGMEM uchar shared_hid_report[] = {
     0x75, 0x10,       //     Report Size (16)
 #    endif
     0x81, 0x06, //     Input (Data, Variable, Relative)
-    0xC0,       //   End Collection
-    0xC0,       // End Collection
+
+#    ifdef POINTING_DEVICE_HIRES_SCROLL_ENABLE
+    0xC0, //   End Collection
+#    endif
+
+    0xC0, //   End Collection
+    0xC0, // End Collection
 #endif
 
 #ifdef EXTRAKEY_ENABLE