Apr 3, 2024

Some knowledges, advises and thoughts about Windows Precision Touchpad

Have you touched a recent Windows laptop? The trackpad can do inertial scrolling, just like smartphones or MacBook. I feel this is a must-have feature of trackpads. But QMK_firmware doesn't have it yet. So I investigated the problem area and wrote some codes on QMK. Today I'd like to share the knowledges from the experience with you.

The key is Windows Precision Touchpad. It is also known as PTP (Precision Touchpad Protocol). It is implemented in Windows 8.1. 11 years ago! It is still sketchy but the only way to get inertial scrolling trackpad on Windows. We should manage to do with the sketchy points of PTP.

The comprehensive document from Microsoft is here: Windows Precision Touchpad Implementation Guide

The codes on QMK is here: GitHub diff

In general

In short, PTP is a special "Digitizer" of USB HID. It tells the geometric and physical positions of fingers on the surface of the trackpad. In contrast, "Mouse" of USB HID tells the moved counts. QMK's pointing device is such a thing. The "geometric and physical position" might look unnoticeable but fatally important idea of PTP. Mouse is based on "count". The difference is fatally important.

Some fancy mice can change speed with their switches. The implementation is simple. Just multiplying counts by speed. But in PTP, it is a completely different game because PTP handles inches or centimeters (physical quantity!), not counts. PTP keeps the trackpad dimensions and the fingers should not go out of the bounds. If we just multiply the centimeters by higher speed, there is no way to avoid overflow! And there are many other subtle but show-stopping problems. The dimensions of the trackpad are written in USB HID report descriptor. If a trackpad want to change the dimensions, resetting USB is the only way. And QMK is not prepared to such hack. Resetting USB requires resetting MCU, and much more... Can you hear the sound of the Rube Goldberg machine?

The Rube Goldberg machine might be more larger than you imagine now. Windows requires to edit the registry and reboot to change the two-finger scrolling speed (see here). It reminds me the MS-DOS era. Windows Precision Touchpad is still sketchy after 11 years. But there is no other way. It is no surprise that MacBook increased the market share in the past decade. The speed changing feature of trackpad is a must-have in such environment.

I don't send PR of PTP codes to QMK for these problems. QMK is already a maze of #ifdef macros. Adding a Rube Goldberg machine is not a good idea. Maybe someday Microsoft will fix the sketchy points after decades of neglect. Otherwise, instead of fixing the problems, Microsoft will discontinue Windows itself. (This will happen in our lifetime, I think. Windows is not a profitable business anymore for anyone, just like Solaris. Microsoft invests almost nothing to make it profitable after two decades, and it is a good decision. We are living in the waning days of PC. Microsoft cannot save PC.)

Anyway Windows barely can do. macOS? No, nothing. There is no way to implement third-party inertial scrolling trackpads. This is why we haven't seen any third-party trackpads for macOS for the past decade. On macOS, an USB PTP trackpad works just as a plain old pointing device which emulates a mouse.

I also investigated Linux. It can do with PTP. Two-finger scrolling is available. But not inertial, so far.

Undocumented behaviors of Windows

The PTP document from Microsoft is very long, but lacks some critical informations.

USB descriptor: Once Windows recognizes that a VENDOR_ID / PRODUCT_ID pair does not have PTP, Windows never finds PTP in the VENDOR_ID / PRODUCT_ID pair anymore.

In other words, we must use unique VENDOR_ID / PRODUCT_ID pair for PTP. Usually we don't care about VENDOR_ID / PRODUCT_ID pair while debugging. It is not OK when we debug PTP device. You can erase the history by USBDeview.

HID report descriptor: Physical and Logical Maximum of Scan Time should be 32-bit width.

Excerpt from Sample Report Descriptors:

The value itself is 16-bit but Physical and Logical Maximum should be 32-bit width.

HID report descriptor: USAGE_PAGE(Button 0x09), USAGE_(Button 1 0x01) is mandatory.

Excerpt from Sample Report Descriptors:

The document says it is optional. But in my experience, it is mandatory. If a HID report descriptor lacks it, Windows becomes BSOD. Just always set UP (false) in the report.

A behavior of Linux

I tried to get the complete picture of Linux USB HID stack from the source code, but failed. I couldn't find the origin of SET_FEATURE request in the source code. The knowledge below is fragmental from my experience.

HID report descriptor: USAGE_PAGE(Digitizers 0x0D), USAGE_(Tip Pressure 0x30) makes PTP device ignored.

We should not use Tip Pressure if we need to do with Linux. I couldn't find the advantages of Tip Pressure in Windows.

QMK source code for PTP implementation

Let's see several points of my codes.

PRECISION_TOUCHPAD_ENABLE and Azoteq IQS5xx

common_features.mk

config.h

rules.mk

The codes in quantum/pointing_device/ of QMK is not available for PTP. I implemented just Azoteq IQS5xx.

EEPROM entry for speed value

eeconfig.c

As I wrote above, the speed changing feature is a Rube Goldberg machine. It requires non-volatile memory to save speed value.

HID report descriptor array is variable (not const).

usb_descriptor.c

This is for the speed changing feature. As I wrote above, I need to change the HID report descriptor dynamically.

Mark and overwrite by magic number in HID report descriptor array

usb_descriptor.c

usb_descriptor.c

usb_descriptor.c

Quick hack. If the magic number collides to other values in the array, it will be a disaster.

RDY line is mandatory.

config.h

precision_touchpad_drivers.c

RDY line is good for fast response and power savings. If you dare to do without RDY line, see pointing_device_driver.c.

My advises

If you are going to make a precision touchpad with Azoteq IQS5xx, there is no problem. Merge the code above to yours.

If not Azoteq IQS5xx, it can be cumbersome. But there is no such multi-touch trackpad module on the market (ICs are a lot. Module is rare). Then you are going to design your own trackpad module. So cumbersome but easy for you.

If you are going to implement another USB HID protocol (there are quite a lot of vendor specific protocol!), get an USB Sniffer box. I bought this one. I don't believe that I could wrote the code above without the box.

My thoughts

QMK is perfect for ATmega32U4, i.e. Pro Micro. But now, we need new codebase for 32-bit MCUs like Cortex-M0+ or RISC-V. With their rich computing resources, initializing after MCU reset can be much richer. It can add much more flexibility to keyboard firmware design and reduce the complexity of codes. C is going to be obsolete in the long run. Zig is a good candidate to replace C in this area.

USB HID lacks good support for programming. HID report descriptor should correspond to the report struct, but no tool can check the correspondence. In the ecosystem of C, such a tool is hard to maintain and use. Zig looks promising in this viewpoint.

No comments:

Post a Comment