| /**************************************************************************** |
| ** |
| ** Copyright (C) 2018 The Qt Company Ltd. |
| ** Contact: https://www.qt.io/licensing/ |
| ** |
| ** This file is part of the test suite of the Qt Toolkit. |
| ** |
| ** $QT_BEGIN_LICENSE:GPL-EXCEPT$ |
| ** Commercial License Usage |
| ** Licensees holding valid commercial Qt licenses may use this file in |
| ** accordance with the commercial license agreement provided with the |
| ** Software or, alternatively, in accordance with the terms contained in |
| ** a written agreement between you and The Qt Company. For licensing terms |
| ** and conditions see https://www.qt.io/terms-conditions. For further |
| ** information use the contact form at https://www.qt.io/contact-us. |
| ** |
| ** GNU General Public License Usage |
| ** Alternatively, this file may be used under the terms of the GNU |
| ** General Public License version 3 as published by the Free Software |
| ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT |
| ** included in the packaging of this file. Please review the following |
| ** information to ensure the GNU General Public License requirements will |
| ** be met: https://www.gnu.org/licenses/gpl-3.0.html. |
| ** |
| ** $QT_END_LICENSE$ |
| ** |
| ****************************************************************************/ |
| |
| #include "mockcompositor.h" |
| #include <QtGui/QRasterWindow> |
| #include <QtGui/QOpenGLWindow> |
| |
| using namespace MockCompositor; |
| |
| class SeatV5Compositor : public DefaultCompositor { |
| public: |
| explicit SeatV5Compositor() |
| { |
| exec([this] { |
| m_config.autoConfigure = true; |
| |
| removeAll<Seat>(); |
| |
| uint capabilities = MockCompositor::Seat::capability_pointer | MockCompositor::Seat::capability_touch; |
| int version = 5; |
| add<Seat>(capabilities, version); |
| }); |
| } |
| }; |
| |
| class tst_seatv5 : public QObject, private SeatV5Compositor |
| { |
| Q_OBJECT |
| private slots: |
| void cleanup() { QTRY_VERIFY2(isClean(), qPrintable(dirtyMessage())); } |
| void bindsToSeat(); |
| |
| // Pointer tests |
| void createsPointer(); |
| void setsCursorOnEnter(); |
| void usesEnterSerial(); |
| void simpleAxis_data(); |
| void simpleAxis(); |
| void fingerScroll(); |
| void fingerScrollSlow(); |
| void wheelDiscreteScroll(); |
| |
| // Touch tests |
| void createsTouch(); |
| void singleTap(); |
| void singleTapFloat(); |
| void multiTouch(); |
| void multiTouchUpAndMotionFrame(); |
| void tapAndMoveInSameFrame(); |
| }; |
| |
| void tst_seatv5::bindsToSeat() |
| { |
| QCOMPOSITOR_COMPARE(get<Seat>()->resourceMap().size(), 1); |
| QCOMPOSITOR_COMPARE(get<Seat>()->resourceMap().first()->version(), 5); |
| } |
| |
| void tst_seatv5::createsPointer() |
| { |
| QCOMPOSITOR_TRY_COMPARE(pointer()->resourceMap().size(), 1); |
| QCOMPOSITOR_TRY_COMPARE(pointer()->resourceMap().first()->version(), 5); |
| } |
| |
| void tst_seatv5::setsCursorOnEnter() |
| { |
| QRasterWindow window; |
| window.resize(64, 64); |
| window.show(); |
| QCOMPOSITOR_TRY_VERIFY(xdgSurface() && xdgSurface()->m_committedConfigureSerial); |
| |
| exec([=] { |
| auto *surface = xdgSurface()->m_surface; |
| pointer()->sendEnter(surface, {0, 0}); |
| pointer()->sendFrame(surface->resource()->client()); |
| }); |
| |
| QCOMPOSITOR_TRY_VERIFY(pointer()->cursorSurface()); |
| } |
| |
| void tst_seatv5::usesEnterSerial() |
| { |
| QSignalSpy setCursorSpy(exec([=] { return pointer(); }), &Pointer::setCursor); |
| QRasterWindow window; |
| window.resize(64, 64); |
| window.show(); |
| QCOMPOSITOR_TRY_VERIFY(xdgSurface() && xdgSurface()->m_committedConfigureSerial); |
| |
| uint enterSerial = exec([=] { |
| return pointer()->sendEnter(xdgSurface()->m_surface, {0, 0}); |
| }); |
| QCOMPOSITOR_TRY_VERIFY(pointer()->cursorSurface()); |
| |
| QTRY_COMPARE(setCursorSpy.count(), 1); |
| QCOMPARE(setCursorSpy.takeFirst().at(0).toUInt(), enterSerial); |
| } |
| |
| 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()) |
| { |
| } |
| const Qt::ScrollPhase phase{}; |
| const QPoint pixelDelta; |
| const QPoint angleDelta; // eights of a degree, positive is upwards, left |
| const Qt::MouseEventSource source{}; |
| }; |
| QVector<Event> m_events; |
| }; |
| |
| void tst_seatv5::simpleAxis_data() |
| { |
| QTest::addColumn<uint>("axis"); |
| QTest::addColumn<qreal>("value"); |
| QTest::addColumn<QPoint>("angleDelta"); |
| |
| // 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}; |
| } |
| |
| void tst_seatv5::simpleAxis() |
| { |
| QFETCH(uint, axis); |
| QFETCH(qreal, value); |
| QFETCH(QPoint, angleDelta); |
| |
| WheelWindow window; |
| QCOMPOSITOR_TRY_VERIFY(xdgSurface() && xdgSurface()->m_committedConfigureSerial); |
| |
| exec([=] { |
| auto *p = pointer(); |
| p->sendEnter(xdgToplevel()->surface(), {32, 32}); |
| p->sendFrame(client()); |
| p->sendAxis( |
| client(), |
| Pointer::axis(axis), |
| value // Length of vector in surface-local space. i.e. positive is downwards |
| ); |
| p->sendFrame(client()); |
| }); |
| |
| QTRY_VERIFY(!window.m_events.empty()); |
| { |
| auto e = window.m_events.takeFirst(); |
| QCOMPARE(e.phase, Qt::NoScrollPhase); |
| // Pixel delta should only be set if we know it's a high-res input device (which we don't) |
| QCOMPARE(e.pixelDelta, QPoint(0, 0)); |
| // There has been no information about what created the event. |
| // Documentation says not synthesized is appropriate in such cases |
| QCOMPARE(e.source, Qt::MouseEventNotSynthesized); |
| QCOMPARE(e.angleDelta, angleDelta); |
| } |
| |
| // Sending axis_stop is not mandatory when axis source != finger |
| } |
| |
| void tst_seatv5::fingerScroll() |
| { |
| WheelWindow window; |
| QCOMPOSITOR_TRY_VERIFY(xdgSurface() && xdgSurface()->m_committedConfigureSerial); |
| |
| exec([=] { |
| auto *p = pointer(); |
| auto *c = client(); |
| p->sendEnter(xdgToplevel()->surface(), {32, 32}); |
| p->sendFrame(c); |
| p->sendAxisSource(c, Pointer::axis_source_finger); |
| p->sendAxis(c, Pointer::axis_vertical_scroll, 10); |
| p->sendFrame(c); |
| }); |
| |
| QTRY_VERIFY(!window.m_events.empty()); |
| { |
| auto e = window.m_events.takeFirst(); |
| QCOMPARE(e.phase, Qt::ScrollBegin); |
| QCOMPARE(e.angleDelta, QPoint()); |
| QCOMPARE(e.pixelDelta, QPoint()); |
| } |
| |
| QTRY_VERIFY(!window.m_events.empty()); |
| // For some reason we send two ScrollBegins, one for each direction, not sure if this is really |
| // necessary, (could be removed from QtBase, hence the conditional below. |
| if (window.m_events.first().phase == Qt::ScrollBegin) { |
| auto e = window.m_events.takeFirst(); |
| QCOMPARE(e.angleDelta, QPoint()); |
| QCOMPARE(e.pixelDelta, QPoint()); |
| } |
| |
| QTRY_VERIFY(!window.m_events.empty()); |
| { |
| auto e = window.m_events.takeFirst(); |
| QCOMPARE(e.phase, Qt::ScrollUpdate); |
| QVERIFY(qAbs(e.angleDelta.x()) <= qAbs(e.angleDelta.y())); // Vertical scroll |
| // QCOMPARE(e.angleDelta, angleDelta); // TODO: what should this be? |
| QCOMPARE(e.pixelDelta, QPoint(0, 10)); |
| QCOMPARE(e.source, Qt::MouseEventSynthesizedBySystem); // A finger is not a wheel |
| } |
| |
| QTRY_VERIFY(window.m_events.empty()); |
| |
| // Scroll horizontally as well |
| exec([=] { |
| pointer()->sendAxisSource(client(), Pointer::axis_source_finger); |
| pointer()->sendAxis(client(), Pointer::axis_horizontal_scroll, 10); |
| pointer()->sendFrame(client()); |
| }); |
| QTRY_VERIFY(!window.m_events.empty()); |
| { |
| auto e = window.m_events.takeFirst(); |
| QCOMPARE(e.phase, Qt::ScrollUpdate); |
| QVERIFY(qAbs(e.angleDelta.x()) > qAbs(e.angleDelta.y())); // Horizontal scroll |
| QCOMPARE(e.pixelDelta, QPoint(10, 0)); |
| QCOMPARE(e.source, Qt::MouseEventSynthesizedBySystem); // A finger is not a wheel |
| } |
| |
| // Scroll diagonally |
| exec([=] { |
| pointer()->sendAxisSource(client(), Pointer::axis_source_finger); |
| pointer()->sendAxis(client(), Pointer::axis_horizontal_scroll, 10); |
| pointer()->sendAxis(client(), Pointer::axis_vertical_scroll, 10); |
| pointer()->sendFrame(client()); |
| }); |
| QTRY_VERIFY(!window.m_events.empty()); |
| { |
| auto e = window.m_events.takeFirst(); |
| QCOMPARE(e.phase, Qt::ScrollUpdate); |
| QCOMPARE(e.pixelDelta, QPoint(10, 10)); |
| QCOMPARE(e.source, Qt::MouseEventSynthesizedBySystem); // A finger is not a wheel |
| } |
| |
| // For diagonal events, Qt sends an additional compatibility ScrollUpdate event |
| if (window.m_events.first().phase == Qt::ScrollUpdate) { |
| auto e = window.m_events.takeFirst(); |
| QCOMPARE(e.angleDelta, QPoint()); |
| QCOMPARE(e.pixelDelta, QPoint()); |
| } |
| |
| QVERIFY(window.m_events.empty()); |
| |
| // Sending axis_stop is mandatory when axis source == finger |
| exec([=] { |
| pointer()->sendAxisStop(client(), Pointer::axis_vertical_scroll); |
| pointer()->sendFrame(client()); |
| }); |
| |
| QTRY_VERIFY(!window.m_events.empty()); |
| { |
| auto e = window.m_events.takeFirst(); |
| QCOMPARE(e.phase, Qt::ScrollEnd); |
| } |
| } |
| |
| |
| void tst_seatv5::fingerScrollSlow() |
| { |
| WheelWindow window; |
| QCOMPOSITOR_TRY_VERIFY(xdgSurface() && xdgSurface()->m_committedConfigureSerial); |
| |
| exec([=] { |
| auto *p = pointer(); |
| auto *c = client(); |
| p->sendEnter(xdgToplevel()->surface(), {32, 32}); |
| p->sendFrame(c); |
| // Send 10 really small updates |
| for (int i = 0; i < 10; ++i) { |
| p->sendAxisSource(c, Pointer::axis_source_finger); |
| p->sendAxis(c, Pointer::axis_vertical_scroll, 0.1); |
| p->sendFrame(c); |
| } |
| p->sendAxisStop(c, Pointer::axis_vertical_scroll); |
| p->sendFrame(c); |
| }); |
| |
| QTRY_VERIFY(!window.m_events.empty()); |
| QPoint accumulated; |
| while (window.m_events.first().phase != Qt::ScrollEnd) { |
| auto e = window.m_events.takeFirst(); |
| accumulated += e.pixelDelta; |
| QTRY_VERIFY(!window.m_events.empty()); |
| } |
| QCOMPARE(accumulated.y(), 1); |
| } |
| void tst_seatv5::wheelDiscreteScroll() |
| { |
| WheelWindow window; |
| QCOMPOSITOR_TRY_VERIFY(xdgSurface() && xdgSurface()->m_committedConfigureSerial); |
| |
| exec([=] { |
| auto *p = pointer(); |
| auto *c = client(); |
| p->sendEnter(xdgToplevel()->surface(), {32, 32}); |
| p->sendFrame(c); |
| p->sendAxisSource(c, Pointer::axis_source_wheel); |
| 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)); |
| } |
| } |
| |
| void tst_seatv5::createsTouch() |
| { |
| QCOMPOSITOR_TRY_COMPARE(touch()->resourceMap().size(), 1); |
| QCOMPOSITOR_TRY_COMPARE(touch()->resourceMap().first()->version(), 5); |
| } |
| |
| class TouchWindow : public QRasterWindow { |
| public: |
| TouchWindow() |
| { |
| resize(64, 64); |
| show(); |
| } |
| void touchEvent(QTouchEvent *event) override |
| { |
| QRasterWindow::touchEvent(event); |
| 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 QTouchEvent *event) |
| : type(event->type()) |
| , touchPointStates(event->touchPointStates()) |
| , touchPoints(event->touchPoints()) |
| { |
| } |
| const QEvent::Type type{}; |
| const Qt::TouchPointStates touchPointStates{}; |
| const QList<QTouchEvent::TouchPoint> touchPoints; |
| }; |
| QVector<Event> m_events; |
| }; |
| |
| void tst_seatv5::singleTap() |
| { |
| TouchWindow window; |
| QCOMPOSITOR_TRY_VERIFY(xdgSurface() && xdgSurface()->m_committedConfigureSerial); |
| |
| exec([=] { |
| auto *t = touch(); |
| auto *c = client(); |
| t->sendDown(xdgToplevel()->surface(), {32, 32}, 1); |
| t->sendFrame(c); |
| t->sendUp(c, 1); |
| t->sendFrame(c); |
| }); |
| |
| QTRY_VERIFY(!window.m_events.empty()); |
| { |
| auto e = window.m_events.takeFirst(); |
| QCOMPARE(e.type, QEvent::TouchBegin); |
| QCOMPARE(e.touchPointStates, Qt::TouchPointState::TouchPointPressed); |
| QCOMPARE(e.touchPoints.length(), 1); |
| QCOMPARE(e.touchPoints.first().pos(), QPointF(32-window.frameMargins().left(), 32-window.frameMargins().top())); |
| } |
| { |
| auto e = window.m_events.takeFirst(); |
| QCOMPARE(e.type, QEvent::TouchEnd); |
| QCOMPARE(e.touchPointStates, Qt::TouchPointState::TouchPointReleased); |
| QCOMPARE(e.touchPoints.length(), 1); |
| QCOMPARE(e.touchPoints.first().pos(), QPointF(32-window.frameMargins().left(), 32-window.frameMargins().top())); |
| } |
| } |
| |
| void tst_seatv5::singleTapFloat() |
| { |
| TouchWindow window; |
| QCOMPOSITOR_TRY_VERIFY(xdgSurface() && xdgSurface()->m_committedConfigureSerial); |
| |
| exec([=] { |
| auto *t = touch(); |
| auto *c = client(); |
| t->sendDown(xdgToplevel()->surface(), {32.75, 32.25}, 1); |
| t->sendFrame(c); |
| t->sendUp(c, 1); |
| t->sendFrame(c); |
| }); |
| |
| QTRY_VERIFY(!window.m_events.empty()); |
| { |
| auto e = window.m_events.takeFirst(); |
| QCOMPARE(e.type, QEvent::TouchBegin); |
| QCOMPARE(e.touchPointStates, Qt::TouchPointState::TouchPointPressed); |
| QCOMPARE(e.touchPoints.length(), 1); |
| QCOMPARE(e.touchPoints.first().pos(), QPointF(32.75-window.frameMargins().left(), 32.25-window.frameMargins().top())); |
| } |
| { |
| auto e = window.m_events.takeFirst(); |
| QCOMPARE(e.type, QEvent::TouchEnd); |
| QCOMPARE(e.touchPointStates, Qt::TouchPointState::TouchPointReleased); |
| QCOMPARE(e.touchPoints.length(), 1); |
| QCOMPARE(e.touchPoints.first().pos(), QPointF(32.75-window.frameMargins().left(), 32.25-window.frameMargins().top())); |
| } |
| } |
| |
| void tst_seatv5::multiTouch() |
| { |
| TouchWindow window; |
| QCOMPOSITOR_TRY_VERIFY(xdgSurface() && xdgSurface()->m_committedConfigureSerial); |
| |
| exec([=] { |
| auto *t = touch(); |
| auto *c = client(); |
| |
| t->sendDown(xdgToplevel()->surface(), {32, 32}, 0); |
| t->sendDown(xdgToplevel()->surface(), {48, 48}, 1); |
| t->sendFrame(c); |
| |
| // Compositor event order should not change the order of the QTouchEvent::touchPoints() |
| // See QTBUG-77014 |
| t->sendMotion(c, {49, 48}, 1); |
| t->sendMotion(c, {33, 32}, 0); |
| t->sendFrame(c); |
| |
| t->sendUp(c, 0); |
| t->sendFrame(c); |
| |
| t->sendUp(c, 1); |
| t->sendFrame(c); |
| }); |
| |
| QTRY_VERIFY(!window.m_events.empty()); |
| { |
| auto e = window.m_events.takeFirst(); |
| QCOMPARE(e.type, QEvent::TouchBegin); |
| QCOMPARE(e.touchPointStates, Qt::TouchPointState::TouchPointPressed); |
| QCOMPARE(e.touchPoints.length(), 2); |
| |
| QCOMPARE(e.touchPoints[0].state(), Qt::TouchPointState::TouchPointPressed); |
| QCOMPARE(e.touchPoints[0].pos(), QPointF(32-window.frameMargins().left(), 32-window.frameMargins().top())); |
| |
| QCOMPARE(e.touchPoints[1].state(), Qt::TouchPointState::TouchPointPressed); |
| QCOMPARE(e.touchPoints[1].pos(), QPointF(48-window.frameMargins().left(), 48-window.frameMargins().top())); |
| } |
| { |
| auto e = window.m_events.takeFirst(); |
| QCOMPARE(e.type, QEvent::TouchUpdate); |
| QCOMPARE(e.touchPoints.length(), 2); |
| |
| QCOMPARE(e.touchPoints[0].state(), Qt::TouchPointState::TouchPointMoved); |
| QCOMPARE(e.touchPoints[0].pos(), QPointF(33-window.frameMargins().left(), 32-window.frameMargins().top())); |
| |
| QCOMPARE(e.touchPoints[1].state(), Qt::TouchPointState::TouchPointMoved); |
| QCOMPARE(e.touchPoints[1].pos(), QPointF(49-window.frameMargins().left(), 48-window.frameMargins().top())); |
| } |
| { |
| auto e = window.m_events.takeFirst(); |
| QCOMPARE(e.type, QEvent::TouchUpdate); |
| QCOMPARE(e.touchPointStates, Qt::TouchPointState::TouchPointReleased | Qt::TouchPointState::TouchPointStationary); |
| QCOMPARE(e.touchPoints.length(), 2); |
| |
| QCOMPARE(e.touchPoints[0].state(), Qt::TouchPointState::TouchPointReleased); |
| QCOMPARE(e.touchPoints[0].pos(), QPointF(33-window.frameMargins().left(), 32-window.frameMargins().top())); |
| |
| QCOMPARE(e.touchPoints[1].state(), Qt::TouchPointState::TouchPointStationary); |
| QCOMPARE(e.touchPoints[1].pos(), QPointF(49-window.frameMargins().left(), 48-window.frameMargins().top())); |
| } |
| { |
| auto e = window.m_events.takeFirst(); |
| QCOMPARE(e.type, QEvent::TouchEnd); |
| QCOMPARE(e.touchPointStates, Qt::TouchPointState::TouchPointReleased); |
| QCOMPARE(e.touchPoints.length(), 1); |
| QCOMPARE(e.touchPoints[0].state(), Qt::TouchPointState::TouchPointReleased); |
| QCOMPARE(e.touchPoints[0].pos(), QPointF(49-window.frameMargins().left(), 48-window.frameMargins().top())); |
| } |
| } |
| |
| void tst_seatv5::multiTouchUpAndMotionFrame() |
| { |
| TouchWindow window; |
| QCOMPOSITOR_TRY_VERIFY(xdgSurface() && xdgSurface()->m_committedConfigureSerial); |
| |
| exec([=] { |
| auto *t = touch(); |
| auto *c = client(); |
| |
| t->sendDown(xdgToplevel()->surface(), {32, 32}, 0); |
| t->sendDown(xdgToplevel()->surface(), {48, 48}, 1); |
| t->sendFrame(c); |
| |
| // Sending an up event after a frame event, before any motion or down events used to |
| // unnecessarily trigger a workaround for a bug in an old version of Weston. The workaround |
| // would prematurely insert a fake frame event splitting the touch event up into two events. |
| // However, this should only be needed on the up event for the very last touch point. So in |
| // this test we verify that it doesn't unncecessarily break up the events. |
| t->sendUp(c, 0); |
| t->sendMotion(c, {49, 48}, 1); |
| t->sendFrame(c); |
| |
| t->sendUp(c, 1); |
| t->sendFrame(c); |
| }); |
| |
| QTRY_VERIFY(!window.m_events.empty()); |
| { |
| auto e = window.m_events.takeFirst(); |
| QCOMPARE(e.type, QEvent::TouchBegin); |
| QCOMPARE(e.touchPoints[0].state(), Qt::TouchPointState::TouchPointPressed); |
| QCOMPARE(e.touchPoints[1].state(), Qt::TouchPointState::TouchPointPressed); |
| } |
| { |
| auto e = window.m_events.takeFirst(); |
| QCOMPARE(e.type, QEvent::TouchUpdate); |
| QCOMPARE(e.touchPoints.length(), 2); |
| QCOMPARE(e.touchPoints[0].state(), Qt::TouchPointState::TouchPointReleased); |
| QCOMPARE(e.touchPoints[1].state(), Qt::TouchPointState::TouchPointMoved); |
| } |
| { |
| auto e = window.m_events.takeFirst(); |
| QCOMPARE(e.type, QEvent::TouchEnd); |
| QCOMPARE(e.touchPoints.length(), 1); |
| QCOMPARE(e.touchPoints[0].state(), Qt::TouchPointState::TouchPointReleased); |
| } |
| QVERIFY(window.m_events.empty()); |
| } |
| |
| void tst_seatv5::tapAndMoveInSameFrame() |
| { |
| TouchWindow window; |
| QCOMPOSITOR_TRY_VERIFY(xdgSurface() && xdgSurface()->m_committedConfigureSerial); |
| |
| exec([=] { |
| auto *t = touch(); |
| auto *c = client(); |
| |
| t->sendDown(xdgToplevel()->surface(), {32, 32}, 0); |
| t->sendMotion(c, {33, 33}, 0); |
| t->sendFrame(c); |
| |
| // Don't leave touch in a weird state |
| t->sendUp(c, 0); |
| t->sendFrame(c); |
| }); |
| |
| QTRY_VERIFY(!window.m_events.empty()); |
| { |
| auto e = window.m_events.takeFirst(); |
| QCOMPARE(e.type, QEvent::TouchBegin); |
| QCOMPARE(e.touchPoints.size(), 1); |
| QCOMPARE(e.touchPoints[0].state(), Qt::TouchPointState::TouchPointPressed); |
| // Position isn't that important, we just want to make sure we actually get the pressed event |
| } |
| |
| // Make sure we eventually release |
| QTRY_VERIFY(!window.m_events.empty()); |
| QTRY_COMPARE(window.m_events.last().touchPoints.first().state(), Qt::TouchPointState::TouchPointReleased); |
| } |
| |
| QCOMPOSITOR_TEST_MAIN(tst_seatv5) |
| #include "tst_seatv5.moc" |