From 245dcd2b80159d579a09e36bbfee66077ecdb843 Mon Sep 17 00:00:00 2001 From: Vlad Zahorodnii Date: Sun, 27 Nov 2022 15:48:33 +0200 Subject: [PATCH] backends/wayland: Provide a cursor per output It allows us to track cursor state per output and make it more straightforward to introduce cursor layers in general. --- src/backends/wayland/wayland_backend.cpp | 84 ++++-------------------- src/backends/wayland/wayland_backend.h | 24 ------- src/backends/wayland/wayland_output.cpp | 78 ++++++++++++++++++++++ src/backends/wayland/wayland_output.h | 23 +++++++ 4 files changed, 114 insertions(+), 95 deletions(-) diff --git a/src/backends/wayland/wayland_backend.cpp b/src/backends/wayland/wayland_backend.cpp index ad061cf50a..28d48bed06 100644 --- a/src/backends/wayland/wayland_backend.cpp +++ b/src/backends/wayland/wayland_backend.cpp @@ -18,19 +18,14 @@ #include "wayland_output.h" #include "wayland_qpainter_backend.h" -#include "cursor.h" #include "dpmsinputeventfilter.h" #include "input.h" -#include -#include #include #include -#include #include #include #include -#include #include #include @@ -41,7 +36,6 @@ #include #include "../drm/gbm_dmabuf.h" -#include #include #define QSIZE_TO_QPOINT(size) QPointF(size.width(), size.height()) @@ -53,63 +47,6 @@ namespace Wayland using namespace KWayland::Client; -WaylandCursor::WaylandCursor(WaylandBackend *backend) - : m_backend(backend) - , m_surface(backend->display()->compositor()->createSurface()) -{ -} - -WaylandCursor::~WaylandCursor() = default; - -void WaylandCursor::enable() -{ - Q_ASSERT(m_disableCount > 0); - --m_disableCount; - if (m_disableCount == 0) { - install(); - } -} - -void WaylandCursor::disable() -{ - ++m_disableCount; - if (m_disableCount == 1) { - uninstall(); - } -} - -void WaylandCursor::install() -{ - const QImage image = Cursors::self()->currentCursor()->image(); - if (m_disableCount || image.isNull() || image.size().isEmpty()) { - uninstall(); - return; - } - - auto *pointer = m_backend->seat()->pointerDevice()->nativePointer(); - if (!pointer || !pointer->isValid()) { - return; - } - - auto buffer = m_backend->display()->shmPool()->createBuffer(image).toStrongRef(); - wl_buffer *imageBuffer = *buffer.data(); - - pointer->setCursor(m_surface.get(), imageBuffer ? Cursors::self()->currentCursor()->hotspot() : QPoint()); - m_surface->attachBuffer(imageBuffer); - m_surface->setScale(std::ceil(image.devicePixelRatio())); - m_surface->damageBuffer(image.rect()); - m_surface->commit(KWayland::Client::Surface::CommitFlag::None); -} - -void WaylandCursor::uninstall() -{ - auto *pointer = m_backend->seat()->pointerDevice()->nativePointer(); - if (!pointer || !pointer->isValid()) { - return; - } - pointer->hideCursor(); -} - WaylandInputDevice::WaylandInputDevice(KWayland::Client::Keyboard *keyboard, WaylandSeat *seat) : m_seat(seat) , m_keyboard(keyboard) @@ -138,7 +75,19 @@ WaylandInputDevice::WaylandInputDevice(KWayland::Client::Pointer *pointer, Wayla , m_pointer(pointer) { connect(pointer, &Pointer::entered, this, [this](quint32 serial, const QPointF &relativeToSurface) { - m_seat->backend()->cursor()->install(); + WaylandOutput *output = m_seat->backend()->findOutput(m_pointer->enteredSurface()); + Q_ASSERT(output); + output->cursor()->setPointer(m_pointer.get()); + }); + connect(pointer, &Pointer::left, this, [this]() { + // wl_pointer.leave carries the wl_surface, but KWayland::Client::Pointer::left does not. + const auto outputs = m_seat->backend()->outputs(); + for (Output *output : outputs) { + WaylandOutput *waylandOutput = static_cast(output); + if (waylandOutput->cursor()->pointer()) { + waylandOutput->cursor()->setPointer(nullptr); + } + } }); connect(pointer, &Pointer::motion, this, [this](const QPointF &relativeToSurface, quint32 time) { WaylandOutput *output = m_seat->backend()->findOutput(m_pointer->enteredSurface()); @@ -479,7 +428,6 @@ WaylandBackend::~WaylandBackend() destroyOutputs(); - m_waylandCursor.reset(); m_seat.reset(); m_display.reset(); @@ -499,22 +447,16 @@ bool WaylandBackend::initialize() createOutputs(); m_seat = std::make_unique(m_display->seat(), this); - m_waylandCursor = std::make_unique(this); QAbstractEventDispatcher *dispatcher = QAbstractEventDispatcher::instance(); QObject::connect(dispatcher, &QAbstractEventDispatcher::aboutToBlock, m_display.get(), &WaylandDisplay::flush); QObject::connect(dispatcher, &QAbstractEventDispatcher::awake, m_display.get(), &WaylandDisplay::flush); - connect(Cursors::self(), &Cursors::currentCursorChanged, this, [this]() { - m_waylandCursor->install(); - }); connect(this, &WaylandBackend::pointerLockChanged, this, [this](bool locked) { if (locked) { - m_waylandCursor->disable(); m_seat->createRelativePointer(); } else { m_seat->destroyRelativePointer(); - m_waylandCursor->enable(); } }); diff --git a/src/backends/wayland/wayland_backend.h b/src/backends/wayland/wayland_backend.h index c3970e44c7..ad392f8f16 100644 --- a/src/backends/wayland/wayland_backend.h +++ b/src/backends/wayland/wayland_backend.h @@ -23,7 +23,6 @@ #include #include -struct wl_buffer; struct wl_display; struct gbm_device; struct gbm_bo; @@ -56,24 +55,6 @@ class WaylandOutput; class WaylandEglBackend; class WaylandDisplay; -class WaylandCursor -{ -public: - explicit WaylandCursor(WaylandBackend *backend); - ~WaylandCursor(); - - void enable(); - void disable(); - - void install(); - void uninstall(); - -private: - WaylandBackend *const m_backend; - std::unique_ptr m_surface; - int m_disableCount = 0; -}; - class WaylandInputDevice : public InputDevice { Q_OBJECT @@ -218,10 +199,6 @@ public: { return m_seat.get(); } - WaylandCursor *cursor() const - { - return m_waylandCursor.get(); - } bool supportsPointerLock(); void togglePointerLock(); @@ -267,7 +244,6 @@ private: std::unique_ptr m_seat; WaylandEglBackend *m_eglBackend = nullptr; QVector m_outputs; - std::unique_ptr m_waylandCursor; std::unique_ptr m_dpmsFilter; bool m_pointerLockRequested = false; #if HAVE_WAYLAND_EGL diff --git a/src/backends/wayland/wayland_output.cpp b/src/backends/wayland/wayland_output.cpp index b4ca9638e9..30279a27ac 100644 --- a/src/backends/wayland/wayland_output.cpp +++ b/src/backends/wayland/wayland_output.cpp @@ -8,17 +8,22 @@ */ #include "wayland_output.h" #include "core/renderloop_p.h" +#include "cursor.h" #include "wayland_backend.h" #include "wayland_display.h" #include "wayland_server.h" #include +#include #include +#include #include #include #include +#include + namespace KWin { namespace Wayland @@ -27,12 +32,73 @@ namespace Wayland using namespace KWayland::Client; static const int s_refreshRate = 60000; // TODO: can we get refresh rate data from Wayland host? +WaylandCursor::WaylandCursor(WaylandBackend *backend) + : m_backend(backend) + , m_surface(backend->display()->compositor()->createSurface()) +{ +} + +WaylandCursor::~WaylandCursor() = default; + +KWayland::Client::Pointer *WaylandCursor::pointer() const +{ + return m_pointer; +} + +void WaylandCursor::setPointer(KWayland::Client::Pointer *pointer) +{ + if (m_pointer == pointer) { + return; + } + m_pointer = pointer; + if (m_pointer) { + m_pointer->setCursor(m_surface.get(), m_hotspot); + } +} + +void WaylandCursor::enable() +{ + if (!m_enabled) { + m_enabled = true; + update(); + } +} + +void WaylandCursor::disable() +{ + if (m_enabled) { + m_enabled = false; + update(); + } +} + +void WaylandCursor::update() +{ + const QImage image = Cursors::self()->currentCursor()->image(); + if (!m_enabled || Cursors::self()->isCursorHidden() || image.isNull()) { + m_surface->attachBuffer(KWayland::Client::Buffer::Ptr()); + m_surface->commit(KWayland::Client::Surface::CommitFlag::None); + m_hotspot = QPoint(); + } else { + m_surface->attachBuffer(m_backend->display()->shmPool()->createBuffer(image)); + m_surface->setScale(std::ceil(image.devicePixelRatio())); + m_surface->damageBuffer(image.rect()); + m_surface->commit(KWayland::Client::Surface::CommitFlag::None); + m_hotspot = Cursors::self()->currentCursor()->hotspot(); + } + + if (m_pointer) { + m_pointer->setCursor(m_surface.get(), m_hotspot); + } +} + WaylandOutput::WaylandOutput(const QString &name, WaylandBackend *backend) : Output(backend) , m_renderLoop(std::make_unique()) , m_surface(backend->display()->compositor()->createSurface()) , m_xdgShellSurface(backend->display()->xdgShell()->createSurface(m_surface.get())) , m_backend(backend) + , m_cursor(std::make_unique(backend)) { if (KWayland::Client::XdgDecorationManager *manager = m_backend->display()->xdgDecorationManager()) { m_xdgDecoration.reset(manager->getToplevelDecoration(m_xdgShellSurface.get())); @@ -62,6 +128,10 @@ WaylandOutput::WaylandOutput(const QString &name, WaylandBackend *backend) connect(m_xdgShellSurface.get(), &XdgShellSurface::closeRequested, qApp, &QCoreApplication::quit); connect(this, &WaylandOutput::enabledChanged, this, &WaylandOutput::updateWindowTitle); connect(this, &WaylandOutput::dpmsModeChanged, this, &WaylandOutput::updateWindowTitle); + + connect(Cursors::self(), &Cursors::currentCursorChanged, this, [this]() { + m_cursor->update(); + }); } WaylandOutput::~WaylandOutput() @@ -81,6 +151,11 @@ KWayland::Client::Surface *WaylandOutput::surface() const return m_surface.get(); } +WaylandCursor *WaylandOutput::cursor() const +{ + return m_cursor.get(); +} + RenderLoop *WaylandOutput::renderLoop() const { return m_renderLoop.get(); @@ -191,6 +266,7 @@ void WaylandOutput::lockPointer(Pointer *pointer, bool lock) m_hasPointerLock = false; if (surfaceWasLocked) { updateWindowTitle(); + m_cursor->enable(); Q_EMIT m_backend->pointerLockChanged(false); } return; @@ -205,12 +281,14 @@ void WaylandOutput::lockPointer(Pointer *pointer, bool lock) connect(m_pointerLock.get(), &LockedPointer::locked, this, [this]() { m_hasPointerLock = true; updateWindowTitle(); + m_cursor->disable(); Q_EMIT m_backend->pointerLockChanged(true); }); connect(m_pointerLock.get(), &LockedPointer::unlocked, this, [this]() { m_pointerLock.reset(); m_hasPointerLock = false; updateWindowTitle(); + m_cursor->enable(); Q_EMIT m_backend->pointerLockChanged(false); }); } diff --git a/src/backends/wayland/wayland_output.h b/src/backends/wayland/wayland_output.h index b1d05538af..107f7bf53f 100644 --- a/src/backends/wayland/wayland_output.h +++ b/src/backends/wayland/wayland_output.h @@ -32,6 +32,27 @@ namespace Wayland { class WaylandBackend; +class WaylandCursor +{ +public: + explicit WaylandCursor(WaylandBackend *backend); + ~WaylandCursor(); + + KWayland::Client::Pointer *pointer() const; + void setPointer(KWayland::Client::Pointer *pointer); + + void enable(); + void disable(); + void update(); + +private: + WaylandBackend *const m_backend; + KWayland::Client::Pointer *m_pointer = nullptr; + std::unique_ptr m_surface; + QPoint m_hotspot; + bool m_enabled = true; +}; + class WaylandOutput : public Output { Q_OBJECT @@ -46,6 +67,7 @@ public: bool isReady() const; KWayland::Client::Surface *surface() const; + WaylandCursor *cursor() const; void lockPointer(KWayland::Client::Pointer *pointer, bool lock); void resize(const QSize &pixelSize); @@ -63,6 +85,7 @@ private: std::unique_ptr m_pointerLock; std::unique_ptr m_xdgDecoration; WaylandBackend *const m_backend; + std::unique_ptr m_cursor; QTimer m_turnOffTimer; bool m_hasPointerLock = false; bool m_ready = false;