summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDavid Edmundson <davidedmundson@kde.org>2023-01-18 13:44:46 +0000
committerVlad Zahorodnii <vlad.zahorodnii@kde.org>2023-11-06 13:30:49 +0200
commitcf7fb478f5b57ac71181d7450045538de6e24176 (patch)
tree6edb81e395205fa763dc01920a2723a2ceb7e0d3
parent2eff0cfa25d60091c36b78490591bf9509bcc624 (diff)
client: Implement QWheelEvent::inverted
"Natural scrolling" is a setting that makes trackpads act in the inverse; moving up moves content down to mimic behavior of touchscreens. However not all scroll events are used for scrolling, so it can be useful to know the real direction. This was exposed in QWheelEvent it just needs plumbing. While this is technically an implementation of wl_pointer version 9, on the other hand, it fixes the value of QWheelEvent::inverted(), which makes scrolling with touchpad on wayland behave in a more expected fashion, so this can be viewed as a bugfix as well. The patch has been tested for almost a month, no regressions have been noticed. Change-Id: I050b8b3e55796beff33badb7c073c0b93589294e Reviewed-by: Shawn Rutledge <shawn.rutledge@qt.io> Reviewed-by: Eskil Abrahamsen Blomfeldt <eskil.abrahamsen-blomfeldt@qt.io> Reviewed-by: Vlad Zahorodnii <vlad.zahorodnii@kde.org> (cherry picked from commit 6b8a99afa3c6dff60bd4096c2273f8db0e4d1247)
-rw-r--r--src/client/qwaylandinputdevice.cpp36
-rw-r--r--src/client/qwaylandinputdevice_p.h7
-rw-r--r--src/client/qwaylandwindow.cpp4
-rw-r--r--tests/auto/client/seat/tst_seat.cpp39
-rw-r--r--tests/auto/client/shared/coreprotocol.cpp7
-rw-r--r--tests/auto/client/shared/coreprotocol.h3
6 files changed, 72 insertions, 24 deletions
diff --git a/src/client/qwaylandinputdevice.cpp b/src/client/qwaylandinputdevice.cpp
index 51a98ca61..a4f8757e3 100644
--- a/src/client/qwaylandinputdevice.cpp
+++ b/src/client/qwaylandinputdevice.cpp
@@ -383,7 +383,7 @@ QWaylandInputDevice::Touch::~Touch()
}
QWaylandInputDevice::QWaylandInputDevice(QWaylandDisplay *display, int version, uint32_t id)
- : QtWayland::wl_seat(display->wl_registry(), id, qMin(version, 8))
+ : QtWayland::wl_seat(display->wl_registry(), id, qMin(version, 9))
, mQDisplay(display)
, mDisplay(display->wl_display())
{
@@ -879,9 +879,9 @@ class WheelEvent : public QWaylandPointerEvent
public:
WheelEvent(QWaylandWindow *surface, Qt::ScrollPhase phase, ulong timestamp, const QPointF &local,
const QPointF &global, const QPoint &pixelDelta, const QPoint &angleDelta,
- Qt::MouseEventSource source, Qt::KeyboardModifiers modifiers)
+ Qt::MouseEventSource source, Qt::KeyboardModifiers modifiers, bool inverted)
: QWaylandPointerEvent(QEvent::Wheel, phase, surface, timestamp,
- local, global, pixelDelta, angleDelta, source, modifiers)
+ local, global, pixelDelta, angleDelta, source, modifiers, inverted)
{
}
};
@@ -975,8 +975,9 @@ void QWaylandInputDevice::Pointer::pointer_axis_stop(uint32_t time, uint32_t axi
if (!target)
target = focusWindow();
Qt::KeyboardModifiers mods = mParent->modifiers();
+ const bool inverted = mFrameData.verticalAxisInverted || mFrameData.horizontalAxisInverted;
WheelEvent wheelEvent(focusWindow(), Qt::ScrollEnd, mParent->mTime, mSurfacePos, mGlobalPos,
- QPoint(), QPoint(), Qt::MouseEventNotSynthesized, mods);
+ QPoint(), QPoint(), Qt::MouseEventNotSynthesized, mods, inverted);
target->handleMouse(mParent, wheelEvent);
mScrollBeginSent = false;
mScrollDeltaRemainder = QPointF();
@@ -1025,6 +1026,21 @@ void QWaylandInputDevice::Pointer::pointer_axis_value120(uint32_t axis, int32_t
}
}
+void QWaylandInputDevice::Pointer::pointer_axis_relative_direction(uint32_t axis, uint32_t direction)
+{
+ const bool inverted = direction == axis_relative_direction_inverted;
+ switch (axis) {
+ case axis_vertical_scroll:
+ mFrameData.verticalAxisInverted = inverted;
+ break;
+ case axis_horizontal_scroll:
+ mFrameData.horizontalAxisInverted = inverted;
+ break;
+ default:
+ qCWarning(lcQpaWaylandInput) << "wl_pointer.axis_relative_direction: Unknown axis:" << axis;
+ }
+}
+
void QWaylandInputDevice::Pointer::setFrameEvent(QWaylandPointerEvent *event)
{
qCDebug(lcQpaWaylandInput) << "Setting frame event " << event->type;
@@ -1046,6 +1062,8 @@ void QWaylandInputDevice::Pointer::FrameData::resetScrollData()
delta120 = QPoint();
delta = QPointF();
axisSource = axis_source_wheel;
+ horizontalAxisInverted = false;
+ verticalAxisInverted = false;
}
bool QWaylandInputDevice::Pointer::FrameData::hasPixelDelta() const
@@ -1127,7 +1145,7 @@ void QWaylandInputDevice::Pointer::flushScrollEvent()
target->handleMouse(mParent, WheelEvent(focusWindow(), Qt::ScrollBegin, mParent->mTime,
mSurfacePos, mGlobalPos, QPoint(), QPoint(),
Qt::MouseEventNotSynthesized,
- mParent->modifiers()));
+ mParent->modifiers(), false));
mScrollBeginSent = true;
mScrollDeltaRemainder = QPointF();
}
@@ -1136,11 +1154,15 @@ void QWaylandInputDevice::Pointer::flushScrollEvent()
QPoint pixelDelta = mFrameData.pixelDeltaAndError(&mScrollDeltaRemainder);
Qt::MouseEventSource source = mFrameData.wheelEventSource();
+
+ // The wayland protocol has separate horizontal and vertical axes, Qt has just the one inverted flag
+ // Pragmatically it should't come up
+ const bool inverted = mFrameData.verticalAxisInverted || mFrameData.horizontalAxisInverted;
+
qCDebug(lcQpaWaylandInput) << "Flushing scroll event" << phase << pixelDelta << angleDelta;
target->handleMouse(mParent, WheelEvent(focusWindow(), phase, mParent->mTime, mSurfacePos, mGlobalPos,
- pixelDelta, angleDelta, source, mParent->modifiers()));
+ pixelDelta, angleDelta, source, mParent->modifiers(), inverted));
}
-
mFrameData.resetScrollData();
}
diff --git a/src/client/qwaylandinputdevice_p.h b/src/client/qwaylandinputdevice_p.h
index a2019e8c0..4561bdefd 100644
--- a/src/client/qwaylandinputdevice_p.h
+++ b/src/client/qwaylandinputdevice_p.h
@@ -309,6 +309,7 @@ protected:
void pointer_axis_discrete(uint32_t axis, int32_t value) override;
void pointer_frame() override;
void pointer_axis_value120(uint32_t axis, int32_t value120) override;
+ void pointer_axis_relative_direction(uint32_t axis, uint32_t direction) override;
private slots:
void handleFocusDestroyed() { invalidateFocus(); }
@@ -347,6 +348,8 @@ public:
QPointF delta;
QPoint delta120;
axis_source axisSource = axis_source_wheel;
+ bool verticalAxisInverted = false;
+ bool horizontalAxisInverted = false;
void resetScrollData();
bool hasPixelDelta() const;
@@ -420,7 +423,7 @@ public:
ulong timestamp, const QPointF &local, const QPointF &global,
const QPoint &pixelDelta, const QPoint &angleDelta,
Qt::MouseEventSource source,
- Qt::KeyboardModifiers modifiers)
+ Qt::KeyboardModifiers modifiers, bool inverted)
: type(type)
, phase(phase)
, timestamp(timestamp)
@@ -431,6 +434,7 @@ public:
, angleDelta(angleDelta)
, source(source)
, surface(surface)
+ , inverted(inverted)
{}
QEvent::Type type = QEvent::None;
@@ -445,6 +449,7 @@ public:
QPoint angleDelta;
Qt::MouseEventSource source = Qt::MouseEventNotSynthesized;
QPointer<QWaylandWindow> surface;
+ bool inverted = false;
};
#ifndef QT_NO_GESTURES
diff --git a/src/client/qwaylandwindow.cpp b/src/client/qwaylandwindow.cpp
index 15044d660..4841ba95b 100644
--- a/src/client/qwaylandwindow.cpp
+++ b/src/client/qwaylandwindow.cpp
@@ -1161,7 +1161,7 @@ void QWaylandWindow::handleMouse(QWaylandInputDevice *inputDevice, const QWaylan
case QEvent::Wheel:
QWindowSystemInterface::handleWheelEvent(window(), e.timestamp, e.local, e.global,
e.pixelDelta, e.angleDelta, e.modifiers,
- e.phase, e.source, false);
+ e.phase, e.source, e.inverted);
break;
default:
Q_UNREACHABLE();
@@ -1352,7 +1352,7 @@ void QWaylandWindow::handleMouseEventWithDecoration(QWaylandInputDevice *inputDe
QWindowSystemInterface::handleWheelEvent(window(), e.timestamp,
localTranslated, globalTranslated,
e.pixelDelta, e.angleDelta, e.modifiers,
- e.phase, e.source, false);
+ e.phase, e.source, e.inverted);
break;
}
default:
diff --git a/tests/auto/client/seat/tst_seat.cpp b/tests/auto/client/seat/tst_seat.cpp
index 34409c2ca..d3bc1eda3 100644
--- a/tests/auto/client/seat/tst_seat.cpp
+++ b/tests/auto/client/seat/tst_seat.cpp
@@ -17,7 +17,7 @@ public:
removeAll<Seat>();
uint capabilities = MockCompositor::Seat::capability_pointer | MockCompositor::Seat::capability_touch;
- int version = 8;
+ int version = 9;
add<Seat>(capabilities, version);
});
}
@@ -54,13 +54,13 @@ private slots:
void tst_seat::bindsToSeat()
{
QCOMPOSITOR_COMPARE(get<Seat>()->resourceMap().size(), 1);
- QCOMPOSITOR_COMPARE(get<Seat>()->resourceMap().first()->version(), 8);
+ QCOMPOSITOR_COMPARE(get<Seat>()->resourceMap().first()->version(), 9);
}
void tst_seat::createsPointer()
{
QCOMPOSITOR_TRY_COMPARE(pointer()->resourceMap().size(), 1);
- QCOMPOSITOR_TRY_COMPARE(pointer()->resourceMap().first()->version(), 8);
+ QCOMPOSITOR_TRY_COMPARE(pointer()->resourceMap().first()->version(), 9);
}
void tst_seat::setsCursorOnEnter()
@@ -114,10 +114,6 @@ public:
QCOMPARE(event->pixelDelta(), QPoint(0, 0));
}
- // The axis vector of the event is already in surface space, so there is now way to tell
- // whether it is inverted or not.
- QCOMPARE(event->inverted(), false);
-
// We didn't press any buttons
QCOMPARE(event->buttons(), Qt::NoButton);
@@ -131,12 +127,14 @@ public:
, pixelDelta(event->pixelDelta())
, angleDelta(event->angleDelta())
, source(event->source())
+ , inverted(event->inverted())
{
}
Qt::ScrollPhase phase{};
QPoint pixelDelta;
QPoint angleDelta; // eights of a degree, positive is upwards, left
Qt::MouseEventSource source{};
+ bool inverted = false;
};
QList<Event> m_events;
};
@@ -146,13 +144,21 @@ void tst_seat::simpleAxis_data()
QTest::addColumn<uint>("axis");
QTest::addColumn<qreal>("value");
QTest::addColumn<QPoint>("angleDelta");
+ QTest::addColumn<bool>("inverted");
// Directions in regular windows/linux terms (no "natural" scrolling)
- QTest::newRow("down") << uint(Pointer::axis_vertical_scroll) << 1.0 << QPoint{0, -12};
- QTest::newRow("up") << uint(Pointer::axis_vertical_scroll) << -1.0 << QPoint{0, 12};
- QTest::newRow("left") << uint(Pointer::axis_horizontal_scroll) << 1.0 << QPoint{-12, 0};
- QTest::newRow("right") << uint(Pointer::axis_horizontal_scroll) << -1.0 << QPoint{12, 0};
- QTest::newRow("up big") << uint(Pointer::axis_vertical_scroll) << -10.0 << QPoint{0, 120};
+ QTest::newRow("down") << uint(Pointer::axis_vertical_scroll) << 1.0 << QPoint{0, -12} << false;
+ QTest::newRow("up") << uint(Pointer::axis_vertical_scroll) << -1.0 << QPoint{0, 12} << false;
+ QTest::newRow("left") << uint(Pointer::axis_horizontal_scroll) << 1.0 << QPoint{-12, 0} << false;
+ QTest::newRow("right") << uint(Pointer::axis_horizontal_scroll) << -1.0 << QPoint{12, 0} << false;
+ QTest::newRow("up big") << uint(Pointer::axis_vertical_scroll) << -10.0 << QPoint{0, 120} << false;
+
+ // (natural) scrolling
+ QTest::newRow("down inverted") << uint(Pointer::axis_vertical_scroll) << 1.0 << QPoint{0, -12} << true;
+ QTest::newRow("up inverted") << uint(Pointer::axis_vertical_scroll) << -1.0 << QPoint{0, 12} << true;
+ QTest::newRow("left inverted") << uint(Pointer::axis_horizontal_scroll) << 1.0 << QPoint{-12, 0} << true;
+ QTest::newRow("right inverted") << uint(Pointer::axis_horizontal_scroll) << -1.0 << QPoint{12, 0} << true;
+ QTest::newRow("up big inverted") << uint(Pointer::axis_vertical_scroll) << -10.0 << QPoint{0, 120} << true;
}
void tst_seat::simpleAxis()
@@ -160,6 +166,7 @@ void tst_seat::simpleAxis()
QFETCH(uint, axis);
QFETCH(qreal, value);
QFETCH(QPoint, angleDelta);
+ QFETCH(bool, inverted);
WheelWindow window;
QCOMPOSITOR_TRY_VERIFY(xdgSurface() && xdgSurface()->m_committedConfigureSerial);
@@ -173,6 +180,8 @@ void tst_seat::simpleAxis()
Pointer::axis(axis),
value // Length of vector in surface-local space. i.e. positive is downwards
);
+ auto direction = inverted ? Pointer::axis_relative_direction_inverted : Pointer::axis_relative_direction_identical;
+ p->sendAxisRelativeDirection(client(), Pointer::axis(axis), direction);
p->sendFrame(client());
});
@@ -186,6 +195,8 @@ void tst_seat::simpleAxis()
// Documentation says not synthesized is appropriate in such cases
QCOMPARE(e.source, Qt::MouseEventNotSynthesized);
QCOMPARE(e.angleDelta, angleDelta);
+
+ QCOMPARE(e.inverted, inverted);
}
// Sending axis_stop is not mandatory when axis source != finger
@@ -386,6 +397,8 @@ void tst_seat::continuousScroll()
QCOMPARE(e.phase, Qt::NoScrollPhase);
QCOMPARE(e.pixelDelta, QPoint(5, -10));
QCOMPARE(e.source, Qt::MouseEventSynthesizedBySystem); // touchpads are not wheels
+ QCOMPARE(e.inverted, false);
+
}
// Sending axis_stop is not mandatory when axis source != finger
}
@@ -393,7 +406,7 @@ void tst_seat::continuousScroll()
void tst_seat::createsTouch()
{
QCOMPOSITOR_TRY_COMPARE(touch()->resourceMap().size(), 1);
- QCOMPOSITOR_TRY_COMPARE(touch()->resourceMap().first()->version(), 8);
+ QCOMPOSITOR_TRY_COMPARE(touch()->resourceMap().first()->version(), 9);
}
class TouchWindow : public QRasterWindow {
diff --git a/tests/auto/client/shared/coreprotocol.cpp b/tests/auto/client/shared/coreprotocol.cpp
index 64586d413..915b0f914 100644
--- a/tests/auto/client/shared/coreprotocol.cpp
+++ b/tests/auto/client/shared/coreprotocol.cpp
@@ -432,6 +432,13 @@ void Pointer::sendAxisValue120(wl_client *client, QtWaylandServer::wl_pointer::a
send_axis_value120(r->handle, axis, value120);
}
+void Pointer::sendAxisRelativeDirection(wl_client *client, QtWaylandServer::wl_pointer::axis axis, QtWaylandServer::wl_pointer::axis_relative_direction direction)
+{
+ const auto pointerResources = resourceMap().values(client);
+ for (auto *r : pointerResources)
+ send_axis_relative_direction(r->handle, axis, direction);
+}
+
void Pointer::pointer_set_cursor(Resource *resource, uint32_t serial, wl_resource *surface, int32_t hotspot_x, int32_t hotspot_y)
{
Q_UNUSED(resource);
diff --git a/tests/auto/client/shared/coreprotocol.h b/tests/auto/client/shared/coreprotocol.h
index e41c719e8..20a3f2122 100644
--- a/tests/auto/client/shared/coreprotocol.h
+++ b/tests/auto/client/shared/coreprotocol.h
@@ -299,7 +299,7 @@ class Seat : public Global, public QtWaylandServer::wl_seat
{
Q_OBJECT
public:
- explicit Seat(CoreCompositor *compositor, uint capabilities = Seat::capability_pointer | Seat::capability_keyboard | Seat::capability_touch, int version = 8);
+ explicit Seat(CoreCompositor *compositor, uint capabilities = Seat::capability_pointer | Seat::capability_keyboard | Seat::capability_touch, int version = 9);
~Seat() override;
void send_capabilities(Resource *resource, uint capabilities) = delete; // Use wrapper instead
void send_capabilities(uint capabilities) = delete; // Use wrapper instead
@@ -350,6 +350,7 @@ public:
void sendAxisStop(wl_client *client, axis axis);
void sendFrame(wl_client *client);
void sendAxisValue120(wl_client *client, axis axis, int value120);
+ void sendAxisRelativeDirection(wl_client *client, axis axis, axis_relative_direction direction);
Seat *m_seat = nullptr;
QList<uint> m_enterSerials;