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
This commit is contained in:
Vladimir Zolotopupov 2023-08-21 23:05:08 +03:00
parent 792ff84419
commit 361e2c3eba
2 changed files with 18 additions and 5 deletions

View file

@ -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

View file

@ -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)