-
-
Notifications
You must be signed in to change notification settings - Fork 40k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[Feature Request] Hi-res scrolling #17585
Comments
I think this is a really cool concept. Is it for using a rotary encoder as a mouse-style scrollwheel? Could it potentially be mapped to any keys? |
Taking a look at "wheel.doc", I'm not sure how practical this is. Namely, this appears to need custom drivers to support it (namely for registry settings). And those dirvers need to be signed, or the overall security of the system degraded (driver signing disabled, which requires secure boot to be disabled). It's definitely possible to implement, but I'm not sure that it is worth the effort here. |
I can say with confidence this works without drivers other than what's bundled with Windows and Linux. hid-remapper allows one to set a custom scroll multiplier and it works without drivers just fine (tested on Windows 10 and 11, and on KDE Plasma). This what I'm stealing the HID collection tree from, so if I can simply do what it's doing this would work just fine. |
QMK already has scroll wheel support via mouse keys. You can just add My goal here is to add a resolution multiplier for wheel scrolling, as that will allow finer precision than an ordinary scroll wheel allows for (and this works without drivers on Windows Vista and up, or Linux). My motivation is for DRAG_SCROLL on my Ploopy Classic to work at a finer precision. Another idea is to put some kind of tiny trackball on top of a mouse, much like Apple's discontinued Mighty Mouse. I mean, you can already do all of these things with QMK as is, but currently you either need to set the DPI very low (or use an encoder with big notches), because by default on Windows one single scroll wheel notch moves three lines. By setting the resolution multiplier to 40 (120/3) Windows will instead scroll one line for every wheel notch (or |
Microsoft's own mice that took advantage of this had "notchless" scroll wheels whose encoders were much more sensitive than what's standard today. Example: https://www.cnet.com/reviews/microsoft-wireless-laser-mouse-7000-2008-review/ |
I bought a Microsoft Surface Precision Mouse to see how Microsoft implements hi-res scrolling, and gathered some data here. This is on a Windows 11 machine, with the mouse attached over USB. First, I installed the relevant drivers for the mouse, and then disabled "flick" scrolling in the new settings panel for the mouse. I also inverted the scroll wheel for preference, but I don't think that's particularly relevant. Then I unplugged the mouse, and opened the USB port in Wireshark (with the other devices filtered out on the USBPcap level). Once the capture began, I plugged in the mouse, holding it upside down to prevent any tracking activity. Then I played with the scroll wheel, scrolling down a little bit, then up a little bit, then down a little bit again (varying the speed as I went along). I added comments to the packet capture to mark the end of the initialization handshake, and each scroll direction change. What I saw that I found particularly curious is that this mouse actually doesn't send a Although I also should note that when I tried using the mouse on my Pinebook Pro in KDE Plasma (Wayland), neither Firefox nor Chrome nor native Qt apps had any hi-res scrolling support out of the box. I didn't try testing further. The spec says the mouse really shouldn't send hi-res scrolling information until the host sends a command to switch into a fancy enhanced mode, so perhaps libinput doesn't have support for this mouse yet to send that command? I also tried in Chromium and Firefox on Windows Subsystem for Linux, in Wayland, and hi-res scrolling was supported. I think WSL uses libinput as well (but of course Windows is abstracting the input packets), and I know for certain that libinput does normally support hi-res scrolling where allowed. Attached is my wireshark capture. |
Hi guys. I think I should be able to help. As you've already noticed there's some stuff in the report descriptor that's required for this to work. I'd recommend you start with the exact same descriptor HID Remapper uses (the relevant top level collection, you can change the report ID) and only start tweaking it to your needs once you confirm hi-res scroll is working. There's some stuff that's not part of the spec that Windows expects anyway, like the resolution multiplier using 2 bits even if 1 would be enough. Also the logical collection semantics might not be obvious if you're new to HID report descriptors. The important part is the feature report, which is different from input/output reports. It's a sort of configuration variable that the host can read and write. I don't know if QMK currently uses feature reports for anything. In our case the feature report will contain two values - the multipliers for horizontal and vertical scroll. In theory there can be multiple levels of the multiplier, but since in practice the operating systems choose the highest value anyway, I think it makes sense to only have two levels, 1x (legacy behavior) and the maximum, which is 120x. This is what it looks like in the report descriptor:
What this means is that if the relevant bits in the feature report are set to 0 (which should be the default), the multiplier will be 1x. If they are set to 1, it will be 120x. When it's 120x, it means 120 units of scroll correspond to 1 normal/legacy scroll "tick", giving you higher resolution. To recap you need:
An operating system that doesn't know about high resolution scroll will not touch the resolution multiplier in the feature report and will expect 1 unit=1 legacy tick. An operating system that knows about high resolution scroll will write 1 (or whatever the logical maximum is) to the resolution multiplier values in the feature report and will then expect 120 units=1 legacy tick (or whatever the physical maximum is). It's also a good idea to detect USB mounting event and reset the resolution multiplier to the default of 1x, otherwise you run into issues when you reboot from an OS that knows about hi-res scroll to one that doesn't. I'm not familiar with QMK source code, but I'll be happy to answer any questions you might have about HID so please don't hesitate to ask for clarification on any of this. |
This issue has been automatically marked as stale because it has not had activity in the last 90 days. It will be closed in the next 30 days unless it is tagged properly or other activity occurs. |
Hey, thanks for working on this! A lot has gotten in the way of me making more progress, and it's hard to debug this sort of thing when you consider that everything I know about USB and HID I learned from working on this :) but I'll see if I can poke at your branch at some point anyway. Just a random thought: what if you swap the wheel and AC pan entries in the descriptor? I wouldn't expect that to fix anything, but if it changes the axis upon which we get rogue outputs it might point us in the right direction? (Pun not intended, I promise!) |
Wow! That's awesome!! Thank you! I expected it to be about as picky as you're seeing -- that's about what I've experienced with @jfedor2/hid-remapper Most mouse driver software that implements hires scrolling seems to turn it on and off based on what window your input is going into. Logitech is horribly picky about this and has a very limited whitelist of .exe files that it enables hires scrolling on. Microsoft Honestly part of my interest in a "hardware" hires scroll thing like this is to help put pressure on Microsoft to better implement their spec :) |
I don't really understand HID setup, so I am hoping someone can help clarify some things. Looking at the Microsoft
However, the current QMK hierarchy is noticeably different. Instead of Application -> Logical -> Physical, the QMK hierarchy goes Application -> Physical.
I attempted to modify usb_descriptor.c to add an additional logical map and the corresponding Resolution Multiplier collections, but I am now realizing this was a bit too naive. My mouse breaks after updating the HID report structure, which probably needs to be reflected elsewhere.
|
Someone was working on a PR for this feature but I can't seem to find it anymore, what happened to it? |
I just wanted to add my user experience with an apple magic mouse I used a while at work: It was the best scrolling experience I've ever had! It is a touch mouse of course with a smooth trackpad on top. But you could very smoothly scroll the window vertically 1:1 and it felt like the perfect mouse wheel. No indents or "3 lines at a time". Just smooth fast responsive scrolling. And you could "fling" the window content for fast scrolling and then stop it again like it was a weighted wheel. And you could precisely scroll and lift your finger without the scrolling jerking or drifting. So this could also be implemented with two of those "Cirque GlidePoint Circle Trackpads" one for mouse pointer and one for scrolling. PS: I got part of this functionality back with a firefox extension called "ScrollAnywhere" that uses the middle mouse button (ring finger for me) to grab and drag / flick instead of the super awkward thing windows does. But this is not going to work if your middle mouse click is pressing the mouse wheel down. |
@b- I just got a Ploopy Adept hoping to have precision/hi-res scrolling, and I'm saddened to see it's not an option. I'm fairly green with QMK, but this is a priority for me, so I'm happy to help any way I can. Are you stuck on anything in particular? |
As you may have noticed I haven't touched this in a long while. However, I would love help solving this once and for all! I want to warn you, though, I was kind of stuck on everything! I understand USB HID descriptors and the like at a cursory level, having learned virtually everything I know about it from trying to solve this. I also am not very experienced in C/C++, either. That said, the main blockers (at least for me) were that I was getting "weird" cursor movement and it appeared that some input buffer on Windows was getting filled without being properly flushed. The pointer would fly into the corner as soon as I touched the ball, and then the computer would start making short beeps (not alert sounds, but actual beeps) a few times a second. This happened just from moving the cursor around while hidpi was enabled in the firmware. I think I mostly used a patch from someone else's comment in this thread or something like that, but I recall encountering similar issues when I tried to crudely craft the descriptor and reports myself. I assume that some of the messages being generated were malformed, and I don't really remember where I left off with troubleshooting that. I have a hunch that the descriptor and reports weren't matching or something, but I'm not sure and I don't really have any backups of what I was trying besides anything remaining on some lingering branch at https://github.com/b-/qmk_firmware. I know I was looking at Wireshark messages, and I even purchased a Microsoft Sufrace Precision mouse that does support hidpi scrolling to record and compare its own descriptor and reports, but I don't remember where I went from there. (It doesn't help that I lost my job around the time I stopped working on this and have still been unable to find full time employment since then. But I suppose that gives me more time now to potentially pick it back up...) If you come up with anything interesting please ping me, either here and/or on the QMK Discord server (my Discord username is @bri9). I'd be more than excited to test with my old Ploopy Classic. Oh, and I'm sure I already mentioned it more than once in this issue thread, but @jfedor2 has an excellent repository for a different HID device firmware, hid-remapper, with a working and customizable scroll multiplier. This might be a good reference? Good luck! |
Hi, I'm also interested in getting this working eventually, though I don't have much time to work on it right now. Like @b- I also tried implementing this feature a couple years ago, but I can't remember what I was stuck on. I recall these articles by Peter Hutterer (libinput developer) were helpful to me:
Sorry for the noise! |
Realized I still had a local copy of the PR so I checked the log to find the username of the person working on it, some light searching led me to this post on reddit. @drashna did anything ever come of this? If desired I could upload it, but wouldn't want to do so without getting permission first. |
Hey. I'm not familiar with QMK, but I just implemented this using both ST's USB Library and TinyUSB. It seems to be working fine for the hosts I've been able to test on (Windows 10 and 11) but I'm still kinda confused. Here's my report descriptor for reference:
Other than making sure that you follow the correct collection hierarchy in the report descriptor, the key to getting it to work is to use the feature reports correctly. Again, here's the format of the feature report:
Since the resolution multiplier usages have a logical range of 0 to 1, I interpret them as really just saying whether the wheels are in high-resolution mode. I haven't played around with them too much, but I think the physical maximums are the actual resolution. Here's the important part: In addition to preparing for a Set_Report request with the feature report, you also need to configure your device to respond to a Get_Report request for the feature report. The Get_Report request preceeds the Set_Report request. I think this is totally undocumented and I only figured it out by analyzing the Wireshark capture that Bri shared. I highly recommend everyone take a look at the capture themselves. What I'm still confused about is why? If there was just one Get_Report request, I'd think it's the host just asking for the capabilities of the device, and then the Set_Report requests that follow are just configuring the device. But for some reason every Set_Report request seems to be preceded by a Get_Report request. In all my testing so far, all the Set_Report requests I've seen just echo what my device sent in response to the Get_Report request. How many times have I said Get_Report and Set_Report by now? Right now, I just have the feature report I respond with hard coded with both scroll_resolution and pan_resolution set. But if and when the host sets values other than what I initially reported, am I supposed to echo that or just respond with the same thing I was sending before? A couple more notes:
|
Hi everyone. I'm not sure what the progress on this is, but I wanted to report that, with the help of the descriptor from @justinjradi, I was able to get a simple smooth scrolling example working. Please see this commit for the details. I also initially ran into the same angry computer beeping issue that @b- mentioned, but I think that this can be fixed by just throttling the rate at which mouse reports are sent to a reasonable rate (e.g. 60Hz). Where exactly you do this is pretty application-dependent, but I did this by using a simple timer check in my drag-scroll implementation:
A few things that ought to be improved, not necessarily by me:
I am a complete novice when it comes to the nitty-gritty of QMK, and I've also never touched USB HID before this in my life. So any support in polishing this feature up would be immensely appreciated. Maybe we can also push for this to be integrated into the official QMK repo? Although I'm not sure how that really works, I've never contributed to an open source project before. |
@morgannewellsun I pulled your changes and confirm they work wonderfully, you're a wizard |
Thanks so much! Great timing - I got my first trackball without a scroll wheel recently and only had to survive two or three weeks without smooth scrolling. A few things:
|
@IsaacElenbaas When you're talking about the wiggling issues when scrolling quickly, are you using 8 bits or 16 bits for the wheel reports? |
Another issue I still have (on ploopy trackball) is that scrolling only starts after a threshold of 60 is crossed. So the start of scrolling always looks like:
|
@evan-goode I can confirm this libinput behavior, it's same with non QMK device. |
@evan-goode I jinxed it by invoking the hardcoded deadzones 😭 You only need to patch libinput, but I left the kernel patch in there if anyone wants to use it for trackpads/tablets like I do. You don't need to restart X like it says to have the scroll deadzone removed, I assume not Wayland either. |
Update: I discovered that all you need to do to enable high-resolution scrolling is to modify the HID report descriptor. None of the other stuff involving feature report datastructures, report IDs, responding to HID get and set, etc... none of it is actually necessary to get high-resolution scrolling. It seems like that stuff is only needed if you want the keyboard to know whether or not the OS supports high-resolution scrolling. To be honest, I have no idea what I'm doing when it comes to the USB stuff in QMK, so I'm not going to bother with adding that functionality. Other than that... I cleaned things up, added some documentation and a couple example implementations for high-resolution drag scrolling, and opened a PR (my first ever 🤪). It branches off 0.26.0 so it should be much more up-to-date. |
The keyboard needs to know that though. Otherwise it will scroll 120x times too fast if the OS didn't enable high-resolution scrolling. (Or whatever it thinks the multiplier is.) |
I totally agree, but unfortunately with the newest version of QMK, there seem to have been some changes to the way USB reports are handled... the code looks nothing like the stuff that I was hacking on, and to be honest I simply don't know how to work with any of it. Also, my understanding is that implementing the ability for the keyboard to be aware of the OS's selection regarding high-resolution scrolling would need to be done independently for each platform, which is a ton of work, not to mention testing. I don't even have non-ChibiOS platforms to test with... It would also make this whole feature much more delicate and prone to breaking in the future. For my personal use, I'm OK with having the keyboard blindly assume that the OS has enabled high-resolution scrolling - maybe I'll have a key to toggle high-resolution scrolling on/off if it's really necessary. Of course, if anyone who's got more experience working with USB HID stuff can put together a better alternative, that would be even better. |
Keyboard awareness of host OS's choice regarding hires scrolling has been added to ChibiOS, LUFA, and VUSB, although only ChibiOS has been tested. |
I've been playing with the pull request this weekend and it works beautifully on both Linux and Windows. I'm really struggling to get it to function on macOS. The best I can tell is that macOS just does not support high resolution scrolling in the same way that Linux and Windows does. Can anyone confirm this? Or is this user error on my part and there is something necessary needed in order to make it work? Edit: To clarify, I'm certain that the OS is reporting that hires scroll is off, what is unclear to me is if there is a way to tell macOS that this is a "hires" mouse (beyond the hid descriptor from the report descriptor) |
I don't have any macOS devices, so I can't do any testing, but I'd be interested in learning more about what the issue is. When Also, if you'd be willing to do a quick test: with |
Sorry, I realized after I wrote my message that it was ambiguous. I edited my comment right before you commented. To summarize: The os does not report "hires_scroll_on". My question is if there is something more that is required on macOS in order to enable this? (Or if it is just one more thing apple chooses not to implement) |
I'm not 100% sure, but my guess is that it's just Apple not playing nice. If I don't have any experience using macOS, but my gut feeling is that this isn't the kind of thing that Apple would let users tinker with via a setting... I'm not sure if there's third-party software that might be able to fix things - I've seen MOS mentioned in a few places. They claim to offer "smooth scrolling" but it seems like they use that term to refer to interpolation of inputs from a normal scroll wheel. But it also seems like there are some other settings provided - maybe it could help? |
I'd like to be clear my post wasn't directed at you specifically, but more anyone in this thread (or coming from google in the future) had any ideas. I do greatly appreciate your help!
I have tried:
At this point my assumption is that qmk may need a driver (or something similar, in the way that BetterMouse may work) in order to get this behavior. All these applications just offer "smoothing/animation" over the "by-line" scroll wheel events, which is distinct from the hires scrolling Windows and Linux offers. My work is making me switch away from Linux to macOS and I'm remembering all the reasons I ditched it 5 years ago. |
@chrishoage By the way, what platform (chibiOS, LUFA, VUSB) did you test? Thanks! |
chibiOS on a Ploopy Adept |
I took a look at the latest Anyways, my current PR implements |
I think that george-norton started implementing getting/setting feature reports in https://github.com/george-norton/qmk_firmware/tree/multitouch_experiment (see #23243) if that's of any help. I should have clarified that the get_report stuff I was trying was just based soley off of my observations of the behavior of a Windows host. However I'm still confused and maybe just misinterpreted the packets since it looks like you've been able to get things working without it. I wish I were able to see the report descriptor of a magic mouse and/or the traffic between a magic mouse and a Mac to see how viewport navigation is implemented. However, I don't own a Mac either. |
I don't think the lack of macOS support should hold up the open PR, it is great as is, and any improvements to macOS can be made later. |
@chrishoage If you end up having some time to do some testing, would you be able to try the following?
|
Actually I had to add When I compile with out it I get this error
The only way for me to get it to compile has been
Omitting both lead to the error above.
I'll cherry pick this to my scratch branch I've been hacking on things and try it out! |
Regarding the undefined macro errors - I realized that I put the preprocessor directives for the default values of those macros in the wrong .h file. The fix for this is located in commit 225fc629926a164a617ebd098ab3d612f16c2cf2, which has been applied to the most up-to-date versions of both my |
The best I can tell Please forgive my testing methodology, I'm hesitant to install qmk on my work computer in order to use In ploopyco.c I've taken your hires dragscroll example and done this (with
with with a fresh pull of I'm happy to test anything further you wish! |
I was able to replicate the issue on Windows. I'm not sure how that slipped past me - I think I failed to flash the keyboard I was testing with, but thought I flashed it... Anyways, I have rewritten history on the However, it also only works with ChibiOS devices where If that's the case for your device, I'd really appreciate if you could give it a try when you get a chance. |
I have done a fresh pull of I also confirmed I was using the correct code by setting Perhaps it's really as simple as apple just don't fully implement HID standards? |
Thanks a lot for helping me test this. This test illustrates a few interesting things about USB HID (I'd imagine that this is common knowledge to those who professionally implement this stuff, but it's interesting to me at least.) If I got anything wrong, please correct me! Preliminary: feature reports are bidirectional and can be sent both ways between the device and computer. In the case of hires scrolling, feature reports are used to communicate regarding whether hires scrolling is used or not. When a HID descriptor uses a resolution multiplier, but doesn't assign that resolution multiplier its own report ID, then the computer seems to assume that it is free to select whatever value it wants. In the case of hires scroll on Windows and Linux, it selects This behavior makes sense. The conputer specifies what report it is requesting from the device via the report ID, so if the resolution multiplier feature report doesn't have its own report ID, then the device has no way to disambiguate between the computer requesting a mouse input report or a resolution multiplier feature report. So it makes sense that the computer just never requests a resolution multiplier feature report, since it has no way to. When the resolution multiplier is given its own report ID (independent of the mouse/shared report ID), the computer behaves differently, and more bizzarely. The computer, upon connecting to the device, requests a resolution multiplier feature report from the device. The computer behaves differently depending on whether or not the request is handled properly. If the request is not handled properly and the computer doesn't receive a resolution multiplier feature report back, after a certain time (timeout?number of attempts?) the computer stops trying and defaults to a value of But when the request is handled properly by the keyboard... The computer ignores the actual contents of the resolution multiplier feature report the keyboard sends, and still chooses whatever it wants? I don't understand why I'm seeing this behavior. Anyways, all of that info is kind of irrelevant and only provided in case someone else decides to try improving things... responding to As a side note, what kind of Linux are you testing with? I have not been able to get smooth scrolling working on my Ubuntu computer, but maybe I'm using an outdated version? |
100% okay thank you for putting in a solid attempt. macOS is stupid and I think this work is great on Linux and Windows - still worth it with those alone!
I'm using arch, I also applied the patch from this comment #17585 (comment) With out it I was seeing the initial "jump" in values when I began scrolling, but after that scrolling was hires |
Hey, just wanted to pipe in with some experience getting high-resolution scrolling working for a non-QMK (but also open-source) keyboard. We've also had mixed experience with Linux, with at least one person finding no change. I don't (yet) have any idea what might be behind this difference, but I figured it'd be useful to know that it's also being encountered elsewhere with a completely different code base. And it seems this is true for macOS as well... @eynsai's comment above might have helped narrow down the issue for macOS, though it doesn't explain why one person has reported it working while another reported otherwise in the case of my patched firmware. Regarding the hypothesized behaviour regarding report IDs, I don't remember seeing anything like this when I was looking through the kernel's source code. From memory, it just instructs the HID device to enable the scroll multiplier if it sees the feature in the device report. I actually think there's also a minor bug in how it handles this case, compared both against what makes sense from reading through the relevant portions of the HID specs and what Windows does, but this is my first foray into USB anything so someone who does this more regularly will have a more reliable assessment than mine. In case it's useful to anyone on Arch or an Arch-derived distro, I recently set up an AUR package for a patched libinput. It has the same effect as @IsaacElenbaas's patch (for libinput only). |
Feature Request Type
Description
Windows and Linux support a "resolution modifier" report for scrolling, which allows finer scrolling instead of just "ticks." Specifically, one can do smooth per-pixel scrolling. It also seems to use fewer resources, possibly because of the larger descriptors.
Unfortunately, it seems that there's virtually no public code or documentation available. The following is everything I've managed to gather in terms of documentation and examples:
This almost certainly requires MOUSE_EXTENDED_REPORT to be enabled based on my testing.
I started mocking up something here: https://github.com/b-/qmk_firmware/tree/resolution_multiplier
However this is the first time I've ever gotten this close to USB or to HID descriptors.
I haven't tested my above code yet, either, though I will as soon as I get the chance.
Anyway, I'm posting this here to help me keep track of this, and of my progress (or lack thereof). I expect to edit this post frequently.
The text was updated successfully, but these errors were encountered: