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.
This commit is contained in:
Vlad Zahorodnii 2022-11-27 15:48:33 +02:00
parent 68517a5806
commit 245dcd2b80
4 changed files with 114 additions and 95 deletions

View file

@ -18,19 +18,14 @@
#include "wayland_output.h"
#include "wayland_qpainter_backend.h"
#include "cursor.h"
#include "dpmsinputeventfilter.h"
#include "input.h"
#include <KWayland/Client/buffer.h>
#include <KWayland/Client/compositor.h>
#include <KWayland/Client/keyboard.h>
#include <KWayland/Client/pointer.h>
#include <KWayland/Client/pointerconstraints.h>
#include <KWayland/Client/pointergestures.h>
#include <KWayland/Client/relativepointer.h>
#include <KWayland/Client/seat.h>
#include <KWayland/Client/shm_pool.h>
#include <KWayland/Client/surface.h>
#include <KWayland/Client/touch.h>
@ -41,7 +36,6 @@
#include <unistd.h>
#include "../drm/gbm_dmabuf.h"
#include <cmath>
#include <drm_fourcc.h>
#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<WaylandOutput *>(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<WaylandSeat>(m_display->seat(), this);
m_waylandCursor = std::make_unique<WaylandCursor>(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();
}
});

View file

@ -23,7 +23,6 @@
#include <QPoint>
#include <QSize>
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<KWayland::Client::Surface> 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<WaylandSeat> m_seat;
WaylandEglBackend *m_eglBackend = nullptr;
QVector<WaylandOutput *> m_outputs;
std::unique_ptr<WaylandCursor> m_waylandCursor;
std::unique_ptr<DpmsInputEventFilter> m_dpmsFilter;
bool m_pointerLockRequested = false;
#if HAVE_WAYLAND_EGL

View file

@ -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 <KWayland/Client/compositor.h>
#include <KWayland/Client/pointer.h>
#include <KWayland/Client/pointerconstraints.h>
#include <KWayland/Client/shm_pool.h>
#include <KWayland/Client/surface.h>
#include <KWayland/Client/xdgdecoration.h>
#include <KLocalizedString>
#include <cmath>
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<RenderLoop>())
, m_surface(backend->display()->compositor()->createSurface())
, m_xdgShellSurface(backend->display()->xdgShell()->createSurface(m_surface.get()))
, m_backend(backend)
, m_cursor(std::make_unique<WaylandCursor>(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);
});
}

View file

@ -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<KWayland::Client::Surface> 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<KWayland::Client::LockedPointer> m_pointerLock;
std::unique_ptr<KWayland::Client::XdgDecoration> m_xdgDecoration;
WaylandBackend *const m_backend;
std::unique_ptr<WaylandCursor> m_cursor;
QTimer m_turnOffTimer;
bool m_hasPointerLock = false;
bool m_ready = false;