From f1e71327d462d2dae0b46677bbc478afb0d1b2f7 Mon Sep 17 00:00:00 2001 From: Vlad Zahorodnii Date: Mon, 24 Oct 2022 22:13:33 +0300 Subject: Client: Add support for high-resolution scrolling With wl_pointer version 8, the axis_discrete event is replaced with the axis_value120 event. The main difference between axis_discrete and axis_value120 is that the latter carries scroll deltas that can be fractions of 120, e.g. 30, etc. See also https://gitlab.freedesktop.org/wayland/wayland/-/merge_requests/72 Change-Id: I4f724ead7ba146dde6d8975fa4edfcfca761769d Reviewed-by: David Edmundson Reviewed-by: Qt CI Bot Reviewed-by: Shawn Rutledge --- tests/auto/client/CMakeLists.txt | 1 + tests/auto/client/seat/tst_seat.cpp | 52 ++++++------ tests/auto/client/seatv7/CMakeLists.txt | 13 +++ tests/auto/client/seatv7/tst_seatv7.cpp | 130 ++++++++++++++++++++++++++++++ tests/auto/client/shared/coreprotocol.cpp | 7 ++ tests/auto/client/shared/coreprotocol.h | 3 +- 6 files changed, 182 insertions(+), 24 deletions(-) create mode 100644 tests/auto/client/seatv7/CMakeLists.txt create mode 100644 tests/auto/client/seatv7/tst_seatv7.cpp (limited to 'tests/auto') diff --git a/tests/auto/client/CMakeLists.txt b/tests/auto/client/CMakeLists.txt index dd5c6e5bd..749e6b83b 100644 --- a/tests/auto/client/CMakeLists.txt +++ b/tests/auto/client/CMakeLists.txt @@ -17,6 +17,7 @@ if (NOT WEBOS) add_subdirectory(output) add_subdirectory(primaryselectionv1) add_subdirectory(seatv4) + add_subdirectory(seatv7) add_subdirectory(seat) add_subdirectory(surface) add_subdirectory(tabletv2) diff --git a/tests/auto/client/seat/tst_seat.cpp b/tests/auto/client/seat/tst_seat.cpp index 68749486b..8a9375d68 100644 --- a/tests/auto/client/seat/tst_seat.cpp +++ b/tests/auto/client/seat/tst_seat.cpp @@ -18,7 +18,7 @@ public: removeAll(); uint capabilities = MockCompositor::Seat::capability_pointer | MockCompositor::Seat::capability_touch; - int version = 7; + int version = 8; add(capabilities, version); }); } @@ -40,8 +40,7 @@ private slots: void fingerScroll(); void fingerScrollSlow(); void continuousScroll(); - void wheelDiscreteScroll_data(); - void wheelDiscreteScroll(); + void highResolutionScroll(); // Touch tests void createsTouch(); @@ -56,13 +55,13 @@ private slots: void tst_seat::bindsToSeat() { QCOMPOSITOR_COMPARE(get()->resourceMap().size(), 1); - QCOMPOSITOR_COMPARE(get()->resourceMap().first()->version(), 7); + QCOMPOSITOR_COMPARE(get()->resourceMap().first()->version(), 8); } void tst_seat::createsPointer() { QCOMPOSITOR_TRY_COMPARE(pointer()->resourceMap().size(), 1); - QCOMPOSITOR_TRY_COMPARE(pointer()->resourceMap().first()->version(), 7); + QCOMPOSITOR_TRY_COMPARE(pointer()->resourceMap().first()->version(), 8); } void tst_seat::setsCursorOnEnter() @@ -320,28 +319,38 @@ void tst_seat::fingerScrollSlow() QCOMPARE(accumulated.y(), -1); } -void tst_seat::wheelDiscreteScroll_data() -{ - QTest::addColumn("source"); - QTest::newRow("wheel") << uint(Pointer::axis_source_wheel); - QTest::newRow("wheel tilt") << uint(Pointer::axis_source_wheel_tilt); -} - -void tst_seat::wheelDiscreteScroll() +void tst_seat::highResolutionScroll() { WheelWindow window; QCOMPOSITOR_TRY_VERIFY(xdgSurface() && xdgSurface()->m_committedConfigureSerial); - QFETCH(uint, source); - exec([=] { auto *p = pointer(); auto *c = client(); p->sendEnter(xdgToplevel()->surface(), {32, 32}); p->sendFrame(c); - p->sendAxisSource(c, Pointer::axis_source(source)); - p->sendAxisDiscrete(c, Pointer::axis_vertical_scroll, 1); // 1 click downwards - p->sendAxis(c, Pointer::axis_vertical_scroll, 1.0); + p->sendAxisSource(c, Pointer::axis_source_wheel); + p->sendAxisValue120(c, Pointer::axis_vertical_scroll, 30); // quarter of a click + p->sendAxis(c, Pointer::axis_vertical_scroll, 3.75); + p->sendFrame(c); + }); + + QTRY_VERIFY(!window.m_events.empty()); + { + auto e = window.m_events.takeFirst(); + QCOMPARE(e.phase, Qt::NoScrollPhase); + QVERIFY(qAbs(e.angleDelta.x()) <= qAbs(e.angleDelta.y())); // Vertical scroll + QCOMPARE(e.angleDelta, QPoint(0, -30)); + // Click scrolls are not continuous and should not have a pixel delta + QCOMPARE(e.pixelDelta, QPoint(0, 0)); + } + + exec([=] { + auto *p = pointer(); + auto *c = client(); + p->sendAxisSource(c, Pointer::axis_source_wheel); + p->sendAxisValue120(c, Pointer::axis_vertical_scroll, 90); // complete the click + p->sendAxis(c, Pointer::axis_vertical_scroll, 11.25); p->sendFrame(c); }); @@ -350,10 +359,7 @@ void tst_seat::wheelDiscreteScroll() auto e = window.m_events.takeFirst(); QCOMPARE(e.phase, Qt::NoScrollPhase); QVERIFY(qAbs(e.angleDelta.x()) <= qAbs(e.angleDelta.y())); // Vertical scroll - // According to the docs the angle delta is in eights of a degree and most mice have - // 1 click = 15 degrees. The angle delta should therefore be: - // 15 degrees / (1/8 eights per degrees) = 120 eights of degrees. - QCOMPARE(e.angleDelta, QPoint(0, -120)); + QCOMPARE(e.angleDelta, QPoint(0, -90)); // Click scrolls are not continuous and should not have a pixel delta QCOMPARE(e.pixelDelta, QPoint(0, 0)); } @@ -388,7 +394,7 @@ void tst_seat::continuousScroll() void tst_seat::createsTouch() { QCOMPOSITOR_TRY_COMPARE(touch()->resourceMap().size(), 1); - QCOMPOSITOR_TRY_COMPARE(touch()->resourceMap().first()->version(), 7); + QCOMPOSITOR_TRY_COMPARE(touch()->resourceMap().first()->version(), 8); } class TouchWindow : public QRasterWindow { diff --git a/tests/auto/client/seatv7/CMakeLists.txt b/tests/auto/client/seatv7/CMakeLists.txt new file mode 100644 index 000000000..cf3ade8c7 --- /dev/null +++ b/tests/auto/client/seatv7/CMakeLists.txt @@ -0,0 +1,13 @@ +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause + +##################################################################### +## tst_seatv7 Test: +##################################################################### + +qt_internal_add_test(tst_seatv7 + SOURCES + tst_seatv7.cpp + LIBRARIES + SharedClientTest +) diff --git a/tests/auto/client/seatv7/tst_seatv7.cpp b/tests/auto/client/seatv7/tst_seatv7.cpp new file mode 100644 index 000000000..c341ec967 --- /dev/null +++ b/tests/auto/client/seatv7/tst_seatv7.cpp @@ -0,0 +1,130 @@ +// Copyright (C) 2018 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +#include "mockcompositor.h" +#include +#include +#include + +using namespace MockCompositor; + +class SeatCompositor : public DefaultCompositor { +public: + explicit SeatCompositor() + { + exec([this] { + m_config.autoConfigure = true; + + removeAll(); + + uint capabilities = MockCompositor::Seat::capability_pointer | MockCompositor::Seat::capability_touch; + int version = 7; + add(capabilities, version); + }); + } +}; + +class tst_seatv7 : public QObject, private SeatCompositor +{ + Q_OBJECT +private slots: + void cleanup() { QTRY_VERIFY2(isClean(), qPrintable(dirtyMessage())); } + void bindsToSeat(); + + // Pointer tests + void wheelDiscreteScroll_data(); + void wheelDiscreteScroll(); +}; + +void tst_seatv7::bindsToSeat() +{ + QCOMPOSITOR_COMPARE(get()->resourceMap().size(), 1); + QCOMPOSITOR_COMPARE(get()->resourceMap().first()->version(), 7); +} + +class WheelWindow : QRasterWindow { +public: + WheelWindow() + { + resize(64, 64); + show(); + } + void wheelEvent(QWheelEvent *event) override + { + QRasterWindow::wheelEvent(event); +// qDebug() << event << "angleDelta" << event->angleDelta() << "pixelDelta" << event->pixelDelta(); + + if (event->phase() != Qt::ScrollUpdate && event->phase() != Qt::NoScrollPhase) { + // Shouldn't have deltas in the these phases + QCOMPARE(event->angleDelta(), QPoint(0, 0)); + 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); + + m_events.append(Event{event}); + } + struct Event // Because I didn't find a convenient way to copy it entirely + { + explicit Event() = default; + explicit Event(const QWheelEvent *event) + : phase(event->phase()) + , pixelDelta(event->pixelDelta()) + , angleDelta(event->angleDelta()) + , source(event->source()) + { + } + Qt::ScrollPhase phase{}; + QPoint pixelDelta; + QPoint angleDelta; // eights of a degree, positive is upwards, left + Qt::MouseEventSource source{}; + }; + QList m_events; +}; + +void tst_seatv7::wheelDiscreteScroll_data() +{ + QTest::addColumn("source"); + QTest::newRow("wheel") << uint(Pointer::axis_source_wheel); + QTest::newRow("wheel tilt") << uint(Pointer::axis_source_wheel_tilt); +} + +void tst_seatv7::wheelDiscreteScroll() +{ + WheelWindow window; + QCOMPOSITOR_TRY_VERIFY(xdgSurface() && xdgSurface()->m_committedConfigureSerial); + + QFETCH(uint, source); + + exec([=] { + auto *p = pointer(); + auto *c = client(); + p->sendEnter(xdgToplevel()->surface(), {32, 32}); + p->sendFrame(c); + p->sendAxisSource(c, Pointer::axis_source(source)); + p->sendAxisDiscrete(c, Pointer::axis_vertical_scroll, 1); // 1 click downwards + p->sendAxis(c, Pointer::axis_vertical_scroll, 1.0); + p->sendFrame(c); + }); + + QTRY_VERIFY(!window.m_events.empty()); + { + auto e = window.m_events.takeFirst(); + QCOMPARE(e.phase, Qt::NoScrollPhase); + QVERIFY(qAbs(e.angleDelta.x()) <= qAbs(e.angleDelta.y())); // Vertical scroll + // According to the docs the angle delta is in eights of a degree and most mice have + // 1 click = 15 degrees. The angle delta should therefore be: + // 15 degrees / (1/8 eights per degrees) = 120 eights of degrees. + QCOMPARE(e.angleDelta, QPoint(0, -120)); + // Click scrolls are not continuous and should not have a pixel delta + QCOMPARE(e.pixelDelta, QPoint(0, 0)); + } +} + +QCOMPOSITOR_TEST_MAIN(tst_seatv7) +#include "tst_seatv7.moc" diff --git a/tests/auto/client/shared/coreprotocol.cpp b/tests/auto/client/shared/coreprotocol.cpp index 005151f74..4f3d21d8e 100644 --- a/tests/auto/client/shared/coreprotocol.cpp +++ b/tests/auto/client/shared/coreprotocol.cpp @@ -423,6 +423,13 @@ void Pointer::sendFrame(wl_client *client) send_frame(r->handle); } +void Pointer::sendAxisValue120(wl_client *client, QtWaylandServer::wl_pointer::axis axis, int value120) +{ + const auto pointerResources = resourceMap().values(client); + for (auto *r : pointerResources) + send_axis_value120(r->handle, axis, value120); +} + 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 370b8cd84..74f07afbf 100644 --- a/tests/auto/client/shared/coreprotocol.h +++ b/tests/auto/client/shared/coreprotocol.h @@ -296,7 +296,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 = 7); + explicit Seat(CoreCompositor *compositor, uint capabilities = Seat::capability_pointer | Seat::capability_keyboard | Seat::capability_touch, int version = 8); ~Seat() override; void send_capabilities(Resource *resource, uint capabilities) = delete; // Use wrapper instead void send_capabilities(uint capabilities) = delete; // Use wrapper instead @@ -346,6 +346,7 @@ public: void sendAxisSource(wl_client *client, axis_source source); void sendAxisStop(wl_client *client, axis axis); void sendFrame(wl_client *client); + void sendAxisValue120(wl_client *client, axis axis, int value120); Seat *m_seat = nullptr; QList m_enterSerials; -- cgit v1.2.3