diff options
-rw-r--r-- | src/client/qwaylandcursor.cpp | 7 | ||||
-rw-r--r-- | src/client/qwaylandcursor_p.h | 2 | ||||
-rw-r--r-- | src/client/qwaylandinputdevice.cpp | 38 | ||||
-rw-r--r-- | src/client/qwaylandinputdevice_p.h | 4 | ||||
-rw-r--r-- | tests/auto/client/seatv4/tst_seatv4.cpp | 31 |
5 files changed, 74 insertions, 8 deletions
diff --git a/src/client/qwaylandcursor.cpp b/src/client/qwaylandcursor.cpp index 8b2ed036d..165df7762 100644 --- a/src/client/qwaylandcursor.cpp +++ b/src/client/qwaylandcursor.cpp @@ -209,7 +209,7 @@ wl_cursor *QWaylandCursorTheme::requestCursor(WaylandCursor shape) return nullptr; } -struct wl_cursor_image *QWaylandCursorTheme::cursorImage(Qt::CursorShape shape) +::wl_cursor_image *QWaylandCursorTheme::cursorImage(Qt::CursorShape shape, uint millisecondsIntoAnimation) { struct wl_cursor *waylandCursor = nullptr; @@ -227,8 +227,9 @@ struct wl_cursor_image *QWaylandCursorTheme::cursorImage(Qt::CursorShape shape) return nullptr; } - struct wl_cursor_image *image = waylandCursor->images[0]; - struct wl_buffer *buffer = wl_cursor_image_get_buffer(image); + int frame = wl_cursor_frame(waylandCursor, millisecondsIntoAnimation); + ::wl_cursor_image *image = waylandCursor->images[frame]; + ::wl_buffer *buffer = wl_cursor_image_get_buffer(image); if (!buffer) { qCWarning(lcQpaWayland) << "Could not find buffer for cursor"; return nullptr; diff --git a/src/client/qwaylandcursor_p.h b/src/client/qwaylandcursor_p.h index 6c48fb628..a4605f3d2 100644 --- a/src/client/qwaylandcursor_p.h +++ b/src/client/qwaylandcursor_p.h @@ -75,7 +75,7 @@ class Q_WAYLAND_CLIENT_EXPORT QWaylandCursorTheme public: static QWaylandCursorTheme *create(QWaylandShm *shm, int size, const QString &themeName); ~QWaylandCursorTheme(); - struct wl_cursor_image *cursorImage(Qt::CursorShape shape); + ::wl_cursor_image *cursorImage(Qt::CursorShape shape, uint millisecondsIntoAnimation = 0); private: enum WaylandCursor { diff --git a/src/client/qwaylandinputdevice.cpp b/src/client/qwaylandinputdevice.cpp index 8231dd9c3..04aefa166 100644 --- a/src/client/qwaylandinputdevice.cpp +++ b/src/client/qwaylandinputdevice.cpp @@ -191,6 +191,27 @@ QWaylandInputDevice::Pointer::~Pointer() #if QT_CONFIG(cursor) +class WlCallback : public QtWayland::wl_callback { +public: + explicit WlCallback(::wl_callback *callback, std::function<void(uint32_t)> fn, bool autoDelete = false) + : QtWayland::wl_callback(callback) + , m_fn(fn) + , m_autoDelete(autoDelete) + {} + ~WlCallback() override { wl_callback_destroy(object()); } + bool done() const { return m_done; } + void callback_done(uint32_t callback_data) override { + m_done = true; + m_fn(callback_data); + if (m_autoDelete) + delete this; + } +private: + bool m_done = false; + std::function<void(uint32_t)> m_fn; + bool m_autoDelete = false; +}; + class CursorSurface : public QWaylandSurface { public: @@ -213,7 +234,7 @@ public: } // Size and hotspot are in surface coordinates - void update(wl_buffer *buffer, const QPoint &hotspot, const QSize &size, int bufferScale) + void update(wl_buffer *buffer, const QPoint &hotspot, const QSize &size, int bufferScale, bool animated = false) { // Calling code needs to ensure buffer scale is supported if != 1 Q_ASSERT(bufferScale == 1 || m_version >= 3); @@ -230,6 +251,13 @@ public: attach(buffer, 0, 0); damage(0, 0, size.width(), size.height()); + m_frameCallback.reset(); + if (animated) { + m_frameCallback.reset(new WlCallback(frame(), [this](uint32_t time){ + Q_UNUSED(time); + m_pointer->updateCursor(); + })); + } commit(); } @@ -242,6 +270,7 @@ public: } private: + QScopedPointer<WlCallback> m_frameCallback; QWaylandInputDevice::Pointer *m_pointer = nullptr; uint m_version = 0; uint m_setSerial = 0; @@ -320,12 +349,14 @@ void QWaylandInputDevice::Pointer::updateCursor() updateCursorTheme(); // Set from shape using theme - if (struct ::wl_cursor_image *image = mCursor.theme->cursorImage(shape)) { + uint time = seat()->mCursor.animationTimer.elapsed(); + if (struct ::wl_cursor_image *image = mCursor.theme->cursorImage(shape, time)) { struct wl_buffer *buffer = wl_cursor_image_get_buffer(image); int bufferScale = mCursor.themeBufferScale; QPoint hotspot = QPoint(image->hotspot_x, image->hotspot_y) / bufferScale; QSize size = QSize(image->width, image->height) / bufferScale; - getOrCreateCursorSurface()->update(buffer, hotspot, size, bufferScale); + bool animated = image->delay > 0; + getOrCreateCursorSurface()->update(buffer, hotspot, size, bufferScale, animated); return; } @@ -515,6 +546,7 @@ void QWaylandInputDevice::setCursor(const QCursor *cursor, const QSharedPointer< mCursor.shape = cursor ? cursor->shape() : Qt::ArrowCursor; mCursor.hotspot = cursor ? cursor->hotSpot() : QPoint(); mCursor.fallbackOutputScale = fallbackOutputScale; + mCursor.animationTimer.start(); if (mCursor.shape == Qt::BitmapCursor) { mCursor.bitmapBuffer = cachedBuffer ? cachedBuffer : QWaylandCursor::cursorBitmapBuffer(mQDisplay, cursor); diff --git a/src/client/qwaylandinputdevice_p.h b/src/client/qwaylandinputdevice_p.h index e87de6ae7..f9fa43c92 100644 --- a/src/client/qwaylandinputdevice_p.h +++ b/src/client/qwaylandinputdevice_p.h @@ -69,7 +69,8 @@ #endif #include <QtCore/QDebug> -#include <QPointer> +#include <QtCore/QElapsedTimer> +#include <QtCore/QPointer> #if QT_CONFIG(cursor) struct wl_cursor_image; @@ -153,6 +154,7 @@ private: Qt::CursorShape shape = Qt::ArrowCursor; int fallbackOutputScale = 1; QPoint hotspot; + QElapsedTimer animationTimer; } mCursor; #endif diff --git a/tests/auto/client/seatv4/tst_seatv4.cpp b/tests/auto/client/seatv4/tst_seatv4.cpp index 7dc2e727a..77304deaf 100644 --- a/tests/auto/client/seatv4/tst_seatv4.cpp +++ b/tests/auto/client/seatv4/tst_seatv4.cpp @@ -80,6 +80,7 @@ private slots: void bitmapCursor(); void hidpiBitmapCursor(); void hidpiBitmapCursorNonInt(); + void animatedCursor(); #endif }; @@ -554,6 +555,36 @@ void tst_seatv4::hidpiBitmapCursorNonInt() 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) |