diff --git a/wayland_backend.cpp b/wayland_backend.cpp index f5087b6fa5..c216d6e115 100644 --- a/wayland_backend.cpp +++ b/wayland_backend.cpp @@ -30,10 +30,13 @@ along with this program. If not, see . #include #include #include +#include #include #include #include #include +#include +#include #include // Qt #include @@ -151,9 +154,10 @@ WaylandSeat::WaylandSeat(wl_seat *seat, WaylandBackend *backend) , m_pointer(NULL) , m_keyboard(NULL) , m_cursor(NULL) - , m_theme(NULL) + , m_theme(new WaylandCursorTheme(backend, this)) , m_enteredSerial(0) , m_backend(backend) + , m_installCursor(false) { m_seat->setup(seat); connect(m_seat, &Seat::hasKeyboardChanged, this, @@ -196,6 +200,10 @@ WaylandSeat::WaylandSeat(wl_seat *seat, WaylandBackend *backend) connect(m_pointer, &Pointer::entered, this, [this](quint32 serial) { m_enteredSerial = serial; + if (!m_installCursor) { + // explicitly hide cursor + wl_pointer_set_cursor(*m_pointer, m_enteredSerial, nullptr, 0, 0); + } } ); connect(m_pointer, &Pointer::motion, this, @@ -251,7 +259,6 @@ WaylandSeat::~WaylandSeat() { destroyPointer(); destroyKeyboard(); - destroyTheme(); } void WaylandSeat::destroyPointer() @@ -268,6 +275,9 @@ void WaylandSeat::destroyKeyboard() void WaylandSeat::installCursorImage(wl_buffer *image, const QSize &size, const QPoint &hotSpot) { + if (!m_installCursor) { + return; + } if (!m_pointer || !m_pointer->isValid()) { return; } @@ -285,25 +295,39 @@ void WaylandSeat::installCursorImage(wl_buffer *image, const QSize &size, const void WaylandSeat::installCursorImage(Qt::CursorShape shape) { - if (!m_theme) { - loadTheme(); - } - wl_cursor *c = wl_cursor_theme_get_cursor(m_theme, Cursor::self()->cursorName(shape).constData()); - if (!c || c->image_count <= 0) { + wl_cursor_image *image = m_theme->get(shape); + if (!image) { return; } - wl_cursor_image *image = c->images[0]; installCursorImage(wl_cursor_image_get_buffer(image), QSize(image->width, image->height), QPoint(image->hotspot_x, image->hotspot_y)); } -void WaylandSeat::loadTheme() +void WaylandSeat::setInstallCursor(bool install) +{ + // TODO: remove, add? + m_installCursor = install; +} + +WaylandCursorTheme::WaylandCursorTheme(WaylandBackend *backend, QObject *parent) + : QObject(parent) + , m_theme(nullptr) + , m_backend(backend) +{ +} + +WaylandCursorTheme::~WaylandCursorTheme() +{ + destroyTheme(); +} + +void WaylandCursorTheme::loadTheme() { Cursor *c = Cursor::self(); if (!m_theme) { // so far the theme had not been created, this means we need to start tracking theme changes - connect(c, SIGNAL(themeChanged()), SLOT(loadTheme())); + connect(c, &Cursor::themeChanged, this, &WaylandCursorTheme::loadTheme); } else { destroyTheme(); } @@ -311,12 +335,92 @@ void WaylandSeat::loadTheme() c->themeSize(), m_backend->shmPool()->shm()); } -void WaylandSeat::destroyTheme() +void WaylandCursorTheme::destroyTheme() { - if (m_theme) { - wl_cursor_theme_destroy(m_theme); - m_theme = NULL; + if (!m_theme) { + return; } + wl_cursor_theme_destroy(m_theme); + m_theme = nullptr; +} + +wl_cursor_image *WaylandCursorTheme::get(Qt::CursorShape shape) +{ + if (!m_theme) { + loadTheme(); + } + wl_cursor *c = wl_cursor_theme_get_cursor(m_theme, Cursor::self()->cursorName(shape).constData()); + if (!c || c->image_count <= 0) { + return nullptr; + } + return c->images[0]; +} + +WaylandCursor::WaylandCursor(Surface *parentSurface, WaylandBackend *backend) + : QObject(backend) + , m_backend(backend) + , m_theme(new WaylandCursorTheme(backend, this)) +{ + auto surface = backend->compositor()->createSurface(this); + m_subSurface = backend->subCompositor()->createSubSurface(QPointer(surface), QPointer(parentSurface), this); + + connect(m_backend->cursorTracker(), &X11CursorTracker::cursorImageChanged, this, + [this](Buffer::Ptr image, const QSize &size, const QPoint &hotspot) { + if (image.isNull()) { + return; + } + setCursorImage(image.toStrongRef()->buffer(), size, hotspot); + } + ); + connect(Cursor::self(), &Cursor::posChanged, this, + [this](const QPoint &pos) { + m_subSurface->setPosition(pos - m_hotSpot); + QPointer parent = m_subSurface->parentSurface(); + if (parent.isNull()) { + return; + } + parent->commit(Surface::CommitFlag::None); + } + ); +} + +void WaylandCursor::setHotSpot(const QPoint &pos) +{ + if (m_hotSpot == pos) { + return; + } + m_hotSpot = pos; + emit hotSpotChanged(m_hotSpot); +} + +void WaylandCursor::setCursorImage(wl_buffer *image, const QSize &size, const QPoint &hotspot) +{ + QPointer cursor = m_subSurface->surface(); + if (cursor.isNull()) { + return; + } + cursor->attachBuffer(image); + cursor->damage(QRect(QPoint(0,0), size)); + cursor->setInputRegion(m_backend->compositor()->createRegion(QRegion()).get()); + cursor->commit(Surface::CommitFlag::None); + setHotSpot(hotspot); + m_subSurface->setPosition(Cursor::pos() - m_hotSpot); + QPointer parent = m_subSurface->parentSurface(); + if (parent.isNull()) { + return; + } + parent->commit(Surface::CommitFlag::None); +} + +void WaylandCursor::setCursorImage(Qt::CursorShape shape) +{ + wl_cursor_image *image = m_theme->get(shape); + if (!image) { + return; + } + setCursorImage(wl_cursor_image_get_buffer(image), + QSize(image->width, image->height), + QPoint(image->hotspot_x, image->hotspot_y)); } WaylandBackend *WaylandBackend::s_self = 0; @@ -346,6 +450,8 @@ WaylandBackend::WaylandBackend(QObject *parent) , m_connectionThreadObject(nullptr) , m_connectionThread(nullptr) , m_fullscreenShell(new FullscreenShell(this)) + , m_subCompositor(new SubCompositor(this)) + , m_cursor(nullptr) { connect(this, &WaylandBackend::shellSurfaceSizeChanged, this, &WaylandBackend::checkBackendReady); connect(m_registry, &Registry::compositorAnnounced, this, @@ -381,6 +487,11 @@ WaylandBackend::WaylandBackend(QObject *parent) m_fullscreenShell->setup(m_registry->bindFullscreenShell(name, version)); } ); + connect(m_registry, &Registry::subCompositorAnnounced, this, + [this](quint32 name, quint32 version) { + m_subCompositor->setup(m_registry->bindSubCompositor(name, version)); + } + ); connect(m_registry, &Registry::interfacesAnnounced, this, &WaylandBackend::createSurface); initConnection(); } @@ -471,10 +582,11 @@ void WaylandBackend::initConnection() void WaylandBackend::installCursorImage(Qt::CursorShape shape) { - if (m_seat.isNull()) { - return; + if (!m_seat.isNull() && m_seat->isInstallCursor()) { + m_seat->installCursorImage(shape); + } else if (m_cursor) { + m_cursor->setCursorImage(shape); } - m_seat->installCursorImage(shape); } void WaylandBackend::createSurface() @@ -484,6 +596,15 @@ void WaylandBackend::createSurface() qCritical() << "Creating Wayland Surface failed"; return; } + if (m_subCompositor->isValid()) { + // we have a sub compositor - let's use it for mouse cursor + m_cursor = new WaylandCursor(m_surface, this); + } else { + // no sub-compositor - use the seat for setting the cursor image + if (m_seat) { + m_seat->setInstallCursor(true); + } + } if (m_fullscreenShell->isValid()) { Output *o = m_outputs.first(); m_fullscreenShell->present(m_surface, o); diff --git a/wayland_backend.h b/wayland_backend.h index 21182edf6d..635d2b51aa 100644 --- a/wayland_backend.h +++ b/wayland_backend.h @@ -29,6 +29,7 @@ along with this program. If not, see . #include class QTemporaryFile; +struct wl_cursor_image; struct wl_cursor_theme; struct wl_buffer; struct wl_display; @@ -52,6 +53,8 @@ class Registry; class Seat; class Shell; class ShellSurface; +class SubCompositor; +class SubSurface; class Surface; } } @@ -101,6 +104,22 @@ private: uint32_t m_lastX11Cursor; }; +class WaylandCursorTheme : public QObject +{ + Q_OBJECT +public: + explicit WaylandCursorTheme(WaylandBackend *backend, QObject *parent = nullptr); + virtual ~WaylandCursorTheme(); + + wl_cursor_image *get(Qt::CursorShape shape); + +private: + void loadTheme(); + void destroyTheme(); + wl_cursor_theme *m_theme; + WaylandBackend *m_backend; +}; + class WaylandSeat : public QObject { Q_OBJECT @@ -110,19 +129,44 @@ public: void installCursorImage(wl_buffer *image, const QSize &size, const QPoint &hotspot); void installCursorImage(Qt::CursorShape shape); -private Q_SLOTS: - void loadTheme(); + void setInstallCursor(bool install); + bool isInstallCursor() const { + return m_installCursor; + } private: void destroyPointer(); void destroyKeyboard(); - void destroyTheme(); KWayland::Client::Seat *m_seat; KWayland::Client::Pointer *m_pointer; KWayland::Client::Keyboard *m_keyboard; KWayland::Client::Surface *m_cursor; - wl_cursor_theme *m_theme; + WaylandCursorTheme *m_theme; uint32_t m_enteredSerial; WaylandBackend *m_backend; + bool m_installCursor; +}; + +class WaylandCursor : public QObject +{ + Q_OBJECT +public: + explicit WaylandCursor(KWayland::Client::Surface *parentSurface, WaylandBackend *backend); + + void setHotSpot(const QPoint &pos); + const QPoint &hotSpot() const { + return m_hotSpot; + } + void setCursorImage(wl_buffer *image, const QSize &size, const QPoint &hotspot); + void setCursorImage(Qt::CursorShape shape); + +Q_SIGNALS: + void hotSpotChanged(const QPoint &); + +private: + WaylandBackend *m_backend; + QPoint m_hotSpot; + KWayland::Client::SubSurface *m_subSurface; + WaylandCursorTheme *m_theme; }; /** @@ -140,6 +184,7 @@ public: KWayland::Client::Compositor *compositor(); const QList &outputs() const; KWayland::Client::ShmPool *shmPool(); + KWayland::Client::SubCompositor *subCompositor(); X11CursorTracker *cursorTracker(); KWayland::Client::Surface *surface() const; @@ -170,6 +215,8 @@ private: KWayland::Client::ConnectionThread *m_connectionThreadObject; QThread *m_connectionThread; KWayland::Client::FullscreenShell *m_fullscreenShell; + KWayland::Client::SubCompositor *m_subCompositor; + WaylandCursor *m_cursor; KWIN_SINGLETON(WaylandBackend) }; @@ -204,6 +251,12 @@ KWayland::Client::Compositor *WaylandBackend::compositor() return m_compositor; } +inline +KWayland::Client::SubCompositor *WaylandBackend::subCompositor() +{ + return m_subCompositor; +} + inline KWayland::Client::ShmPool* WaylandBackend::shmPool() {