Skip to content
This repository has been archived by the owner on Nov 1, 2021. It is now read-only.

High-resolution scroll wheel support #3082

Open
wants to merge 10 commits into
base: master
Choose a base branch
from

Conversation

JoseExposito
Copy link
Contributor

@JoseExposito JoseExposito commented Aug 1, 2021

Description:

Starting with Linux Kernel v5.0 two new axes are available for high-resolution wheel scrolling: REL_WHEEL_HI_RES and REL_HWHEEL_HI_RES.
Both axes send data in fractions of 120 where each multiple of 120 amounts to one logical scroll event. Fractions of 120 indicate a wheel movement less than one detent. A "detent" is the named used in the Kernel for a mouse wheel click:
https://www.kernel.org/doc/html/v5.11-rc7/input/event-codes.html#ev-rel

Three new events are now available on libinput: LIBINPUT_EVENT_POINTER_SCROLL_WHEEL, LIBINPUT_EVENT_POINTER_SCROLL_FINGER, and LIBINPUT_EVENT_POINTER_SCROLL_CONTINUOUS. These events replace the LIBINPUT_EVENT_POINTER_AXIS event, so new clients should simply ignore that event.

This LIBINPUT_EVENT_POINTER_SCROLL_WHEEL event adds a new API libinput_pointer_scroll_get_value_v120(). The libinput_pointer_scroll_get_value_v120() is a mirror from the kernel API (itself a copy of the Windows API). The new event is sent for all wheel events, even those that don't technically support high-resolution scrolling and even on older kernels that don't have this feature. So callers can simply ignore any LIBINPUT_EVENT_POINTER_AXIS event and use the new event types only.

For LIBINPUT_EVENT_POINTER_SCROLL_FINGER and LIBINPUT_EVENT_POINTER_SCROLL_CONTINUOUS, as well as LIBINPUT_EVENT_POINTER_SCROLL_WHEEL, a new API is available: libinput_pointer_scroll_get_value(). This API is similar to libinput_pointer_axis_get_value().

Status

How to test it:

Clone and install my libinput branch:
https://gitlab.freedesktop.org/libinput/libinput/-/merge_requests/652

Clone and install Peter's Wayland protocol:
https://gitlab.freedesktop.org/wayland/wayland/-/merge_requests/72/

Reboot.

tinywl has been updated in this PR, so you can test that scroll is still working with backends that support and don't support high-resolution scroll.

To test with a client that supports high-resolution scroll, clone my GTK branch: https://gitlab.gnome.org/GNOME/gtk/-/merge_requests/3839

And run it:

$ meson build --prefix=`pwd`/build/install --buildtype=debug # Installing here to not replace your stable GTK
$ ninja -C build && \
   ninja -C build install && \
   GDK_BACKEND=wayland WAYLAND_DEBUG=1 XDG_DATA_DIRS=`pwd`/build/install/share:$XDG_DATA_DIRS LD_LIBRARY_PATH=`pwd`/build/install/lib64:$LD_LIBRARY_PATH ./build/install/bin/gtk4-demo

Of course, you'll need hardware to test it.
Logitech mice are supported, otherwise any mouse that shows a ResolutionMultiplier in the hid-recorder output (most Microsoft-branded mice of the last decade).
Or, if you happen to have an Apple Magic Mouse, I added support in the Kernel. You'll need to compile the Kernel with my patches, let me know if you need help.

For code reviewers

It's easier to review this PR commit by commit.

I hesitated a lot about "seat: support lo-res clients using hi-res backends". Placing the accumulators in wlr_seat_client is technically incorrect, there should be an accumulator for each pointer in wlr_seat_client->pointers.

However, given that the accumulator expires after one second of inactivity and given than using 2 or more mice at the same time is a niche use case, I decided to leave the accumulator there for the sake of simplicity. Also, the side effects of using multiple mice at the same time are really difficult to notice.

Keeping the PR as draft until there is a client to test with.

cc @emersion @whot

@emersion
Copy link
Member

emersion commented Aug 1, 2021

Thanks for working on this! In case this hasn't been noticed, @tokyovigilante made a PR for the same feature a few years (!) ago: #1628

@JoseExposito
Copy link
Contributor Author

JoseExposito commented Aug 1, 2021

Hi @emersion, thanks for pointing it out, I didn't noticed there was an existing PR. However, libinput's API has changed a bit. This PR includes every change made until now.

@emersion emersion added the breaking Breaking change in public API label Aug 1, 2021
@tokyovigilante
Copy link
Contributor

Thanks for the @, I'm happy to hand this over to you @JoseExposito and review or test. As you note it had been both a moving target as well as a bit of a chicken and egg scenario waiting for libinput, wayland and GTK to all get their ducks in a row which is why I had parked this.

@JoseExposito JoseExposito force-pushed the hi-res-scrolling branch 2 times, most recently from 2e2b320 to 4e30fb2 Compare August 9, 2021 16:40
@JoseExposito
Copy link
Contributor Author

JoseExposito commented Aug 9, 2021

@tokyovigilante yes... This kind of features can be tricky, because of all the parts involved, hopefully this time we will get it merged 😄

@emersion GTK is ready:
https://gitlab.gnome.org/GNOME/gtk/-/merge_requests/3839

I updated the PR description with build instructions and removed the "Draft" tag. I'll drop a comment on the Wayland MR with all the links.

@JoseExposito JoseExposito marked this pull request as ready for review August 9, 2021 17:28
@JoseExposito
Copy link
Contributor Author

libinput has been merged and 1.19 will be released in less than a month including high-resolution scroll support.

@emersion at this point your feedback on the the Wayland protocol or in this PR would be very valuable, so I can perform the changes in time.

Starting with Linux Kernel v5.0 two new axes are available for
mice that support high-resolution wheel scrolling: REL_WHEEL_HI_RES and
REL_HWHEEL_HI_RES.

Both axes send data in fractions of 120 where each multiple of 120
amounts to one logical scroll event. Fractions of 120 indicate a wheel
movement less than one detent.

Three new events are now available on libinput:
LIBINPUT_EVENT_POINTER_SCROLL_WHEEL,
LIBINPUT_EVENT_POINTER_SCROLL_FINGER, and
LIBINPUT_EVENT_POINTER_SCROLL_CONTINUOUS.
These events replace the LIBINPUT_EVENT_POINTER_AXIS event, so new
clients should simply ignore that event.

Also, two new APIs are available to access the high-resolution data:
libinput_event_pointer_get_scroll_value() and
libinput_event_pointer_get_scroll_value_v120().

Create a new event, wlr_event_pointer_axis_v120, to represent
high-resolution axis events and a signal to emit them:
wlr_pointer->pointer.axis_value120.
Add a project argument (LIBINPUT_HAS_SCROLL_VALUE120) to allow
building against old versions of libinput or, where high-resolution
scroll is available, support it.
On newer versions of libinput, the event LIBINPUT_EVENT_POINTER_AXIS has
been deprecated in favour of LIBINPUT_EVENT_POINTER_SCROLL_WHEEL,
LIBINPUT_EVENT_POINTER_SCROLL_FINGER and
LIBINPUT_EVENT_POINTER_SCROLL_CONTINUOUS.

Where new events are provided by the backend, ignore
LIBINPUT_EVENT_POINTER_AXIS.
Receive high-resolution scroll events from libinput and emit the
appropiate wlr_pointer signal.
Receive high-resolution scroll events from the parent compositor using
a Wayland listiner and emit the appropiate wlr_pointer signal.
Recevie the high-resolution scroll events from the libinput or Wayland
backends, abstracted as pointer signals, and re-emit them from the
cursor interface.
When the selected backend doesn't support high-resolution scroll,
clients requesting high-resolution events must be handled properly.

Upgrade the seat protocol to version 8 and change the defaul pointer
axis handler to support new clients.
When the selected backend supports high-resolution scroll, wlroots will
emit the axis_value120 signal.

Add the required APIs so wlroots compositors can handle this new events
and the default handler implemetion.

For the moment, the default implementation does NOT handle clients that
don't support high-scroll resolution.
When the selected backend supports high-resolution scroll but the client
doesn't, we need to accumulate value120 deltas until we can notify a
discrete event.

In addition, add a expire time so accumulated deltas are reset after a
period of inactivity.
Demostrate how to use the high-resolution scroll API.
@JoseExposito
Copy link
Contributor Author

Updated to use axis_value120 instead of axis_v120 as decided in the protocol MR.

@emersion
Copy link
Member

This introduces a new event, wlr_pointer.events.axis_value120. However unlike the kernel or libinput we don't need to guarantee ABI compatibility. Couldn't we just add a new delta_value120 field to struct wlr_event_pointer_axis instead?

@JoseExposito
Copy link
Contributor Author

@emersion yes, that should also work. I'm not as familiarized with the code as you are, so please me correct me if I'm wrong.

If we remove the new event and add a filed in wlr_event_pointer_axis, we could check is value120 != 0 in wlr_seat_pointer_send_axis and pick the right value, making transparent to clients the handling of high-resolution events.

However, what would happen with compositors using their own wlr_event_pointer_axis handler? Wouldn't this change break scroll because they'd be using the wlr_event_pointer_axis.delta and wlr_event_pointer_axis.delta_discrete fields?

@emersion
Copy link
Member

Hm, is the old axis event trigger less often than the new one? Is it bad to not look at axis120 and just use axis/axis_discrete?

@JoseExposito
Copy link
Contributor Author

is the old axis event trigger less often than the new one?

Yes, when your hardware supports high-resolution scroll, the new event is sent more often. For example, a mouse with an extra mouse wheel "click" will send:

High Res 60
High Res 60
Low Res 120 (discrete 1)

Is it bad to not look at axis120 and just use axis/axis_discrete?

When both compositor and client support a version of the protocol >= 8, clients will ignore the old event in favour of the new one.

That means that if the compositor's custom implementation of wlr_seat_pointer_send_axis doesn't take the right delta value and calls wl_pointer_send_axis_discrete or wl_pointer_send_axis_value120 accordingly, client scroll will break silently.

I think that, sadly, there's no way to not break the API? 🤔

@whot
Copy link

whot commented Sep 27, 2021

a mouse with an extra mouse wheel "click" will send:

An important note here: the mouse may send. There really is no correlation between the two events once they come out of the kernel. logitech devices in particular have some in-kernel wheel handling where some high-resolution events get filtered, so it's not always the same number of high-res events per low-res event.

@emersion
Copy link
Member

emersion commented Nov 1, 2021

wlroots has migrated to gitlab.freedesktop.org. This pull request has been moved to:

https://gitlab.freedesktop.org/wlroots/wlroots/-/merge_requests/3082

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
breaking Breaking change in public API
Development

Successfully merging this pull request may close these issues.

None yet

4 participants