summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/client/qwaylandcursor.cpp7
-rw-r--r--src/client/qwaylandcursor_p.h2
-rw-r--r--src/client/qwaylandinputdevice.cpp38
-rw-r--r--src/client/qwaylandinputdevice_p.h4
-rw-r--r--tests/auto/client/seatv4/tst_seatv4.cpp31
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)