| /**************************************************************************** |
| ** |
| ** 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> |
| #if QT_CONFIG(cursor) |
| #include <wayland-cursor.h> |
| #include <QtGui/private/qguiapplication_p.h> |
| #include <QtWaylandClient/private/qwaylanddisplay_p.h> |
| #include <QtWaylandClient/private/qwaylandintegration_p.h> |
| #endif |
| |
| using namespace MockCompositor; |
| |
| // wl_seat version 5 was introduced in wayland 1.10, and although that's pretty old, |
| // there are still compositors that have yet to update their implementation to support |
| // the new version (most importantly our own QtWaylandCompositor). |
| // As long as that's the case, this test makes sure input events still works on version 4. |
| class SeatV4Compositor : public DefaultCompositor { |
| public: |
| explicit SeatV4Compositor() |
| { |
| exec([this] { |
| m_config.autoConfigure = true; |
| |
| removeAll<Seat>(); |
| |
| uint capabilities = Seat::capability_pointer | Seat::capability_keyboard; |
| int version = 4; |
| add<Seat>(capabilities, version); |
| }); |
| } |
| }; |
| |
| class tst_seatv4 : public QObject, private SeatV4Compositor |
| { |
| Q_OBJECT |
| private slots: |
| void cleanup(); |
| void bindsToSeat(); |
| void keyboardKeyPress(); |
| #if QT_CONFIG(cursor) |
| void createsPointer(); |
| void setsCursorOnEnter(); |
| void usesEnterSerial(); |
| void focusDestruction(); |
| void mousePress(); |
| void mousePressFloat(); |
| void simpleAxis_data(); |
| void simpleAxis(); |
| void invalidPointerEvents(); |
| void scaledCursor(); |
| void unscaledFallbackCursor(); |
| void bitmapCursor(); |
| void hidpiBitmapCursor(); |
| void hidpiBitmapCursorNonInt(); |
| void animatedCursor(); |
| #endif |
| }; |
| |
| void tst_seatv4::cleanup() |
| { |
| QTRY_VERIFY2(isClean(), qPrintable(dirtyMessage())); |
| QCOMPOSITOR_COMPARE(getAll<Output>().size(), 1); // No extra outputs left |
| } |
| |
| void tst_seatv4::bindsToSeat() |
| { |
| QCOMPOSITOR_COMPARE(get<Seat>()->resourceMap().size(), 1); |
| QCOMPOSITOR_COMPARE(get<Seat>()->resourceMap().first()->version(), 4); |
| } |
| |
| void tst_seatv4::keyboardKeyPress() |
| { |
| class Window : public QRasterWindow { |
| public: |
| void keyPressEvent(QKeyEvent *) override { m_pressed = true; } |
| bool m_pressed = false; |
| }; |
| |
| Window window; |
| window.resize(64, 64); |
| window.show(); |
| QCOMPOSITOR_TRY_VERIFY(xdgSurface() && xdgSurface()->m_committedConfigureSerial); |
| |
| uint keyCode = 80; // arbitrarily chosen |
| exec([&] { |
| auto *surface = xdgSurface()->m_surface; |
| keyboard()->sendEnter(surface); |
| keyboard()->sendKey(client(), keyCode, Keyboard::key_state_pressed); |
| keyboard()->sendKey(client(), keyCode, Keyboard::key_state_released); |
| }); |
| QTRY_VERIFY(window.m_pressed); |
| } |
| |
| #if QT_CONFIG(cursor) |
| |
| void tst_seatv4::createsPointer() |
| { |
| QCOMPOSITOR_TRY_COMPARE(pointer()->resourceMap().size(), 1); |
| QCOMPOSITOR_TRY_COMPARE(pointer()->resourceMap().first()->version(), 4); |
| } |
| |
| void tst_seatv4::setsCursorOnEnter() |
| { |
| QRasterWindow window; |
| window.resize(64, 64); |
| window.show(); |
| QCOMPOSITOR_TRY_VERIFY(xdgSurface() && xdgSurface()->m_committedConfigureSerial); |
| |
| exec([=] { pointer()->sendEnter(xdgSurface()->m_surface, {32, 32}); }); |
| QCOMPOSITOR_TRY_VERIFY(cursorSurface()); |
| } |
| |
| void tst_seatv4::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, {32, 32}); |
| }); |
| QCOMPOSITOR_TRY_VERIFY(cursorSurface()); |
| |
| QTRY_COMPARE(setCursorSpy.count(), 1); |
| QCOMPARE(setCursorSpy.takeFirst().at(0).toUInt(), enterSerial); |
| } |
| |
| void tst_seatv4::focusDestruction() |
| { |
| QSignalSpy setCursorSpy(exec([=] { return pointer(); }), &Pointer::setCursor); |
| QRasterWindow window; |
| window.resize(64, 64); |
| window.show(); |
| QCOMPOSITOR_TRY_VERIFY(xdgSurface() && xdgSurface()->m_committedConfigureSerial); |
| // Setting a cursor now is not allowed since there has been no enter event |
| QCOMPARE(setCursorSpy.count(), 0); |
| |
| uint enterSerial = exec([=] { |
| return pointer()->sendEnter(xdgSurface()->m_surface, {32, 32}); |
| }); |
| QCOMPOSITOR_TRY_VERIFY(cursorSurface()); |
| QTRY_COMPARE(setCursorSpy.count(), 1); |
| QCOMPARE(setCursorSpy.takeFirst().at(0).toUInt(), enterSerial); |
| |
| // Destroy the focus |
| window.close(); |
| |
| QRasterWindow window2; |
| window2.resize(64, 64); |
| window2.show(); |
| window2.setCursor(Qt::WaitCursor); |
| QCOMPOSITOR_TRY_VERIFY(xdgSurface() && xdgSurface()->m_committedConfigureSerial); |
| |
| // Setting a cursor now is not allowed since there has been no enter event |
| xdgPingAndWaitForPong(); |
| QCOMPARE(setCursorSpy.count(), 0); |
| } |
| |
| void tst_seatv4::mousePress() |
| { |
| class Window : public QRasterWindow { |
| public: |
| void mousePressEvent(QMouseEvent *) override { m_pressed = true; } |
| bool m_pressed = false; |
| }; |
| |
| Window window; |
| window.resize(64, 64); |
| window.show(); |
| QCOMPOSITOR_TRY_VERIFY(xdgSurface() && xdgSurface()->m_committedConfigureSerial); |
| |
| exec([&] { |
| auto *surface = xdgSurface()->m_surface; |
| pointer()->sendEnter(surface, {32, 32}); |
| pointer()->sendButton(client(), BTN_LEFT, 1); |
| pointer()->sendButton(client(), BTN_LEFT, 0); |
| }); |
| QTRY_VERIFY(window.m_pressed); |
| } |
| |
| void tst_seatv4::mousePressFloat() |
| { |
| class Window : public QRasterWindow { |
| public: |
| void mousePressEvent(QMouseEvent *e) override { m_position = e->localPos(); } |
| QPointF m_position; |
| }; |
| |
| Window window; |
| window.resize(64, 64); |
| window.show(); |
| QCOMPOSITOR_TRY_VERIFY(xdgSurface() && xdgSurface()->m_committedConfigureSerial); |
| |
| exec([&] { |
| auto *surface = xdgSurface()->m_surface; |
| pointer()->sendEnter(surface, {32.75, 32.25}); |
| pointer()->sendButton(client(), BTN_LEFT, 1); |
| pointer()->sendButton(client(), BTN_LEFT, 0); |
| }); |
| QMargins m = window.frameMargins(); |
| QPointF pressedPosition(32.75 -m.left(), 32.25 - m.top()); |
| QTRY_COMPARE(window.m_position, pressedPosition); |
| } |
| |
| void tst_seatv4::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_seatv4::simpleAxis() |
| { |
| QFETCH(uint, axis); |
| QFETCH(qreal, value); |
| QFETCH(QPoint, angleDelta); |
| |
| class WheelWindow : QRasterWindow { |
| public: |
| explicit WheelWindow() |
| { |
| resize(64, 64); |
| show(); |
| } |
| void wheelEvent(QWheelEvent *event) override |
| { |
| QRasterWindow::wheelEvent(event); |
| // Angle delta should always be provided (says docs) |
| QVERIFY(!event->angleDelta().isNull()); |
| |
| // There are now scroll phases on Wayland prior to v5 |
| QCOMPARE(event->phase(), Qt::NoScrollPhase); |
| |
| // Pixel delta should only be set if we know it's a high-res input device (which we don't) |
| 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); |
| |
| // There has been no information about what created the event. |
| // Documentation says not synthesized is appropriate in such cases |
| QCOMPARE(event->source(), Qt::MouseEventNotSynthesized); |
| |
| m_events.append(Event{event->pixelDelta(), event->angleDelta()}); |
| } |
| struct Event // Because I didn't find a convenient way to copy it entirely |
| { |
| Event() = default; |
| |
| const QPoint pixelDelta; |
| const QPoint angleDelta; // eights of a degree, positive is upwards, left |
| }; |
| QVector<Event> m_events; |
| }; |
| |
| WheelWindow window; |
| QCOMPOSITOR_TRY_VERIFY(xdgSurface() && xdgSurface()->m_committedConfigureSerial); |
| |
| exec([=] { |
| Surface *surface = xdgSurface()->m_surface; |
| pointer()->sendEnter(surface, {32, 32}); |
| wl_client *client = surface->resource()->client(); |
| // Length of vector in surface-local space. i.e. positive is downwards |
| pointer()->sendAxis( |
| client, |
| Pointer::axis(axis), |
| value // Length of vector in surface-local space. i.e. positive is downwards |
| ); |
| }); |
| |
| QTRY_COMPARE(window.m_events.size(), 1); |
| auto event = window.m_events.takeFirst(); |
| QCOMPARE(event.angleDelta, angleDelta); |
| } |
| |
| void tst_seatv4::invalidPointerEvents() |
| { |
| QRasterWindow window; |
| window.resize(64, 64); |
| window.show(); |
| QCOMPOSITOR_TRY_VERIFY(xdgSurface() && xdgSurface()->m_committedConfigureSerial); |
| |
| exec([=] { |
| auto *p = pointer(); |
| auto *c = client(); |
| // Purposefully send events without a wl_pointer.enter |
| p->sendMotion(c, {32, 32}); |
| p->sendButton(c, BTN_LEFT, Pointer::button_state_pressed); |
| p->sendAxis(c, Pointer::axis_vertical_scroll, 1.0); |
| }); |
| |
| // Make sure we get here without crashing |
| xdgPingAndWaitForPong(); |
| } |
| |
| static bool supportsCursorSize(uint size, wl_shm *shm) |
| { |
| auto *theme = wl_cursor_theme_load(qgetenv("XCURSOR_THEME"), size, shm); |
| if (!theme) |
| return false; |
| |
| constexpr std::array<const char *, 4> names{"left_ptr", "default", "left_arrow", "top_left_arrow"}; |
| for (const char *name : names) { |
| if (auto *cursor = wl_cursor_theme_get_cursor(theme, name)) { |
| auto *image = cursor->images[0]; |
| return image->width == image->height && image->width == size; |
| } |
| } |
| return false; |
| } |
| |
| static bool supportsCursorSizes(const QVector<uint> &sizes) |
| { |
| auto *waylandIntegration = static_cast<QtWaylandClient::QWaylandIntegration *>(QGuiApplicationPrivate::platformIntegration()); |
| wl_shm *shm = waylandIntegration->display()->shm()->object(); |
| return std::all_of(sizes.begin(), sizes.end(), [=](uint size) { |
| return supportsCursorSize(size, shm); |
| }); |
| } |
| |
| static uint defaultCursorSize() { |
| const int xCursorSize = qEnvironmentVariableIntValue("XCURSOR_SIZE"); |
| return xCursorSize > 0 ? uint(xCursorSize) : 32; |
| } |
| |
| void tst_seatv4::scaledCursor() |
| { |
| const uint defaultSize = defaultCursorSize(); |
| if (!supportsCursorSizes({defaultSize, defaultSize * 2})) |
| QSKIP("Cursor themes with default size and 2x default size not found."); |
| |
| // Add a highdpi output |
| exec([&] { |
| OutputData d; |
| d.scale = 2; |
| d.position = {1920, 0}; |
| add<Output>(d); |
| }); |
| |
| QRasterWindow window; |
| window.resize(64, 64); |
| window.show(); |
| QCOMPOSITOR_TRY_VERIFY(xdgSurface() && xdgSurface()->m_committedConfigureSerial); |
| |
| exec([=] { pointer()->sendEnter(xdgSurface()->m_surface, {32, 32}); }); |
| QCOMPOSITOR_TRY_VERIFY(cursorSurface()); |
| QCOMPOSITOR_TRY_VERIFY(cursorSurface()->m_committed.buffer); |
| QCOMPOSITOR_TRY_COMPARE(cursorSurface()->m_committed.bufferScale, 1); |
| QSize unscaledPixelSize = exec([=] { |
| return cursorSurface()->m_committed.buffer->size(); |
| }); |
| |
| exec([=] { |
| auto *surface = cursorSurface(); |
| surface->sendEnter(getAll<Output>()[1]); |
| surface->sendLeave(getAll<Output>()[0]); |
| }); |
| |
| QCOMPOSITOR_TRY_COMPARE(cursorSurface()->m_committed.buffer->size(), unscaledPixelSize * 2); |
| |
| // Remove the extra output to clean up for the next test |
| exec([&] { remove(output(1)); }); |
| } |
| |
| void tst_seatv4::unscaledFallbackCursor() |
| { |
| const uint defaultSize = defaultCursorSize(); |
| if (!supportsCursorSizes({defaultSize})) |
| QSKIP("Default cursor size not supported"); |
| |
| const int screens = 4; // with scales 1, 2, 4, 8 |
| |
| exec([=] { |
| for (int i = 1; i < screens; ++i) { |
| OutputData d; |
| d.scale = int(qPow(2, i)); |
| d.position = {1920 * i, 0}; |
| add<Output>(d); |
| } |
| }); |
| |
| QRasterWindow window; |
| window.resize(64, 64); |
| window.show(); |
| QCOMPOSITOR_TRY_VERIFY(xdgSurface() && xdgSurface()->m_committedConfigureSerial); |
| exec([=] { pointer()->sendEnter(xdgSurface()->m_surface, {32, 32}); }); |
| QCOMPOSITOR_TRY_VERIFY(cursorSurface()); |
| QCOMPOSITOR_TRY_VERIFY(cursorSurface()->m_committed.buffer); |
| QCOMPOSITOR_TRY_COMPARE(cursorSurface()->m_committed.bufferScale, 1); |
| QSize unscaledPixelSize = exec([=] { |
| return cursorSurface()->m_committed.buffer->size(); |
| }); |
| |
| QCOMPARE(unscaledPixelSize.width(), int(defaultSize)); |
| QCOMPARE(unscaledPixelSize.height(), int(defaultSize)); |
| |
| for (int i = 1; i < screens; ++i) { |
| exec([=] { |
| auto *surface = cursorSurface(); |
| surface->sendEnter(getAll<Output>()[i]); |
| surface->sendLeave(getAll<Output>()[i-1]); |
| }); |
| |
| xdgPingAndWaitForPong(); // Give the client a chance to mess up |
| |
| // Surface size (buffer size / scale) should stay constant |
| QCOMPOSITOR_TRY_COMPARE(cursorSurface()->m_committed.buffer->size() / cursorSurface()->m_committed.bufferScale, unscaledPixelSize); |
| } |
| |
| // Remove the extra outputs to clean up for the next test |
| exec([&] { while (auto *o = output(1)) remove(o); }); |
| } |
| |
| void tst_seatv4::bitmapCursor() |
| { |
| // Add a highdpi output |
| exec([&] { |
| OutputData d; |
| d.scale = 2; |
| d.position = {1920, 0}; |
| add<Output>(d); |
| }); |
| |
| QRasterWindow window; |
| window.resize(64, 64); |
| |
| QPixmap pixmap(24, 24); |
| pixmap.setDevicePixelRatio(1); |
| QPoint hotspot(12, 12); // In device pixel coordinates |
| QCursor cursor(pixmap, hotspot.x(), hotspot.y()); |
| window.setCursor(cursor); |
| |
| window.show(); |
| QCOMPOSITOR_TRY_VERIFY(xdgSurface() && xdgSurface()->m_committedConfigureSerial); |
| |
| exec([=] { pointer()->sendEnter(xdgSurface()->m_surface, {32, 32}); }); |
| QCOMPOSITOR_TRY_VERIFY(cursorSurface()); |
| QCOMPOSITOR_TRY_VERIFY(cursorSurface()->m_committed.buffer); |
| QCOMPOSITOR_COMPARE(cursorSurface()->m_committed.buffer->size(), QSize(24, 24)); |
| QCOMPOSITOR_COMPARE(cursorSurface()->m_committed.bufferScale, 1); |
| QCOMPOSITOR_COMPARE(pointer()->m_hotspot, QPoint(12, 12)); |
| |
| exec([=] { |
| auto *surface = cursorSurface(); |
| surface->sendEnter(getAll<Output>()[1]); |
| surface->sendLeave(getAll<Output>()[0]); |
| }); |
| |
| xdgPingAndWaitForPong(); |
| |
| // Everything should remain the same, the cursor still has dpr 1 |
| QCOMPOSITOR_COMPARE(cursorSurface()->m_committed.bufferScale, 1); |
| QCOMPOSITOR_COMPARE(cursorSurface()->m_committed.buffer->size(), QSize(24, 24)); |
| QCOMPOSITOR_COMPARE(pointer()->m_hotspot, QPoint(12, 12)); |
| |
| // Remove the extra output to clean up for the next test |
| exec([&] { remove(getAll<Output>()[1]); }); |
| } |
| |
| void tst_seatv4::hidpiBitmapCursor() |
| { |
| // Add a highdpi output |
| exec([&] { |
| OutputData d; |
| d.scale = 2; |
| d.position = {1920, 0}; |
| add<Output>(d); |
| }); |
| |
| QRasterWindow window; |
| window.resize(64, 64); |
| |
| QPixmap pixmap(48, 48); |
| pixmap.setDevicePixelRatio(2); |
| QPoint hotspot(12, 12); // In device pixel coordinates |
| QCursor cursor(pixmap, hotspot.x(), hotspot.y()); |
| window.setCursor(cursor); |
| |
| window.show(); |
| QCOMPOSITOR_TRY_VERIFY(xdgSurface() && xdgSurface()->m_committedConfigureSerial); |
| |
| exec([=] { pointer()->sendEnter(xdgSurface()->m_surface, {32, 32}); }); |
| QCOMPOSITOR_TRY_VERIFY(cursorSurface()); |
| QCOMPOSITOR_TRY_VERIFY(cursorSurface()->m_committed.buffer); |
| QCOMPOSITOR_COMPARE(cursorSurface()->m_committed.buffer->size(), QSize(48, 48)); |
| QCOMPOSITOR_COMPARE(cursorSurface()->m_committed.bufferScale, 2); |
| QCOMPOSITOR_COMPARE(pointer()->m_hotspot, QPoint(12, 12)); |
| |
| exec([=] { |
| auto *surface = cursorSurface(); |
| surface->sendEnter(getAll<Output>()[1]); |
| surface->sendLeave(getAll<Output>()[0]); |
| }); |
| |
| xdgPingAndWaitForPong(); |
| |
| QCOMPOSITOR_COMPARE(cursorSurface()->m_committed.bufferScale, 2); |
| QCOMPOSITOR_COMPARE(cursorSurface()->m_committed.buffer->size(), QSize(48, 48)); |
| QCOMPOSITOR_COMPARE(pointer()->m_hotspot, QPoint(12, 12)); |
| |
| // Remove the extra output to clean up for the next test |
| exec([&] { remove(getAll<Output>()[1]); }); |
| } |
| |
| void tst_seatv4::hidpiBitmapCursorNonInt() |
| { |
| QRasterWindow window; |
| window.resize(64, 64); |
| |
| QPixmap pixmap(100, 100); |
| pixmap.setDevicePixelRatio(2.5); // dpr width is now 100 / 2.5 = 40 |
| QPoint hotspot(20, 20); // In device pixel coordinates (middle of buffer) |
| QCursor cursor(pixmap, hotspot.x(), hotspot.y()); |
| window.setCursor(cursor); |
| |
| window.show(); |
| QCOMPOSITOR_TRY_VERIFY(xdgSurface() && xdgSurface()->m_committedConfigureSerial); |
| |
| exec([=] { pointer()->sendEnter(xdgSurface()->m_surface, {32, 32}); }); |
| QCOMPOSITOR_TRY_VERIFY(cursorSurface()); |
| QCOMPOSITOR_TRY_VERIFY(cursorSurface()->m_committed.buffer); |
| QCOMPOSITOR_COMPARE(cursorSurface()->m_committed.buffer->size(), QSize(100, 100)); |
| QCOMPOSITOR_COMPARE(cursorSurface()->m_committed.bufferScale, 2); |
| // Verify that the hotspot was scaled correctly |
| // Surface size is now 100 / 2 = 50, so the middle should be at 25 in surface coordinates |
| QCOMPOSITOR_COMPARE(pointer()->m_hotspot, QPoint(25, 25)); |
| } |
| |
| void tst_seatv4::animatedCursor() |
| { |
| QRasterWindow window; |
| window.resize(64, 64); |
| window.setCursor(Qt::WaitCursor); // TODO: verify that the theme has an animated wait cursor or skip test |
| window.show(); |
| QCOMPOSITOR_TRY_VERIFY(xdgSurface() && xdgSurface()->m_committedConfigureSerial); |
| |
| exec([=] { pointer()->sendEnter(xdgSurface()->m_surface, {32, 32}); }); |
| QCOMPOSITOR_TRY_VERIFY(cursorSurface()); |
| |
| // We should get the first buffer without waiting for a frame callback |
| QCOMPOSITOR_TRY_VERIFY(cursorSurface()->m_committed.buffer); |
| QSignalSpy bufferSpy(exec([=] { return cursorSurface(); }), &Surface::bufferCommitted); |
| |
| exec([&] { |
| // Make sure no extra buffers have arrived |
| QVERIFY(bufferSpy.empty()); |
| |
| // The client should send a frame request in order to time animations correctly |
| QVERIFY(!cursorSurface()->m_waitingFrameCallbacks.empty()); |
| |
| // Tell the client it's time to animate |
| cursorSurface()->sendFrameCallbacks(); |
| }); |
| |
| // Verify that we get a new cursor buffer |
| QTRY_COMPARE(bufferSpy.count(), 1); |
| } |
| |
| #endif // QT_CONFIG(cursor) |
| |
| QCOMPOSITOR_TEST_MAIN(tst_seatv4) |
| #include "tst_seatv4.moc" |