From 361e2c3eba39c7c31b0b4f10fbe688407b1a349a Mon Sep 17 00:00:00 2001 From: Vladimir Zolotopupov Date: Mon, 21 Aug 2023 23:05:08 +0300 Subject: [PATCH] wayland: Fix high-resolution scroll wheel discrete step calculation Some wheels might send fewer or more than 120 fractions per step. In order to always register a discrete step, we count these as a step already at half(120/2) of one mouse wheel step in stepping mode. The timer resets the initial value of the step because the mouse can switch between free spin and stepping mode. When transitioning from free spin to stepping mode, the mouse calibrates its wheel position to the starting point and generates some fractions. As a result, our starting position can appear far from the zero point, potentially causing us to count two(or zero) steps instead of one. Additionally, the compositor might start when the mouse is in free spin mode(or the mouse could be connected in free spin mode), leading to the same issue. https://gitlab.freedesktop.org/libinput/libinput/-/issues/814 --- src/wayland/pointer_interface.cpp | 20 ++++++++++++++++---- src/wayland/pointer_interface_p.h | 3 ++- 2 files changed, 18 insertions(+), 5 deletions(-) diff --git a/src/wayland/pointer_interface.cpp b/src/wayland/pointer_interface.cpp index f9197fadd3..95eb357e88 100644 --- a/src/wayland/pointer_interface.cpp +++ b/src/wayland/pointer_interface.cpp @@ -133,8 +133,12 @@ void PointerInterfacePrivate::sendFrame() } } -bool PointerInterfacePrivate::AxisAccumulator::Axis::shouldReset(int newDirection) const +bool PointerInterfacePrivate::AxisAccumulator::Axis::shouldReset(int newDirection, std::chrono::milliseconds newTimestamp) const { + if (newTimestamp.count() - timestamp.count() >= 1000) { + return true; + } + // Reset the accumulator if the delta has opposite sign. return direction && ((direction < 0) != (newDirection < 0)); } @@ -220,18 +224,26 @@ static void updateAccumulators(Qt::Orientation orientation, qreal delta, qint32 const int newDirection = deltaV120 > 0 ? 1 : -1; auto &accum = d->axisAccumulator.axis(orientation); - if (accum.shouldReset(newDirection)) { + if (accum.shouldReset(newDirection, d->seat->timestamp())) { accum.reset(); } + accum.timestamp = d->seat->timestamp(); accum.direction = newDirection; accum.axis += delta; accum.axis120 += deltaV120; // ±120 is a "wheel click" - valueDiscrete = accum.axis120 / 120; - accum.axis120 -= valueDiscrete * 120; + if (std::abs(accum.axis120) >= 60) { + const int steps = accum.axis120 / 120; + valueDiscrete += steps; + if (steps == 0) { + valueDiscrete += accum.direction; + } + + accum.axis120 -= valueDiscrete * 120; + } if (valueDiscrete) { // Accumulate the axis values to send to low-res clients diff --git a/src/wayland/pointer_interface_p.h b/src/wayland/pointer_interface_p.h index 6a1d0921a9..df566627c3 100644 --- a/src/wayland/pointer_interface_p.h +++ b/src/wayland/pointer_interface_p.h @@ -29,7 +29,7 @@ class PointerInterfacePrivate : public QtWaylandServer::wl_pointer { struct Axis { - bool shouldReset(int newDirection) const; + bool shouldReset(int newDirection, std::chrono::milliseconds newTimestamp) const; void reset() { @@ -40,6 +40,7 @@ class PointerInterfacePrivate : public QtWaylandServer::wl_pointer qint32 axis120 = 0; qreal axis = 0; int direction = 0; + std::chrono::milliseconds timestamp = std::chrono::milliseconds::zero(); }; Axis &axis(Qt::Orientation orientation)