backends/wayland: Use software cursor when pointer is locked
Currently, if the pointer is locked, the wayland backend will create a subsurface. However, when the cursor moves to one of the screen edges, it looks weird because the xdg window geometry is computed as bounding geometry of the main surface and its subsurfaces. However, even after calling xdg_surface.set_window_geometry, it still looks weird because subsurfaces are stacked above window decoration. With the proposed change, when the pointer is locked, the wayland backend will hide the host compositor and use the software cursor. If the pointer is unlocked, it will go back to using host cursor. Long term goal is to make every output have its own cursor to make output layer logic simpler.
This commit is contained in:
parent
2fd9cc4fc1
commit
c784217eae
6 changed files with 43 additions and 165 deletions
|
@ -21,7 +21,6 @@
|
|||
#include "cursor.h"
|
||||
#include "dpmsinputeventfilter.h"
|
||||
#include "input.h"
|
||||
#include "pointer_input.h"
|
||||
|
||||
#include <KWayland/Client/buffer.h>
|
||||
#include <KWayland/Client/compositor.h>
|
||||
|
@ -32,8 +31,6 @@
|
|||
#include <KWayland/Client/relativepointer.h>
|
||||
#include <KWayland/Client/seat.h>
|
||||
#include <KWayland/Client/shm_pool.h>
|
||||
#include <KWayland/Client/subcompositor.h>
|
||||
#include <KWayland/Client/subsurface.h>
|
||||
#include <KWayland/Client/surface.h>
|
||||
#include <KWayland/Client/touch.h>
|
||||
|
||||
|
@ -58,127 +55,59 @@ using namespace KWayland::Client;
|
|||
|
||||
WaylandCursor::WaylandCursor(WaylandBackend *backend)
|
||||
: m_backend(backend)
|
||||
, m_surface(backend->display()->compositor()->createSurface())
|
||||
{
|
||||
resetSurface();
|
||||
}
|
||||
|
||||
WaylandCursor::~WaylandCursor() = default;
|
||||
|
||||
void WaylandCursor::resetSurface()
|
||||
void WaylandCursor::enable()
|
||||
{
|
||||
m_surface.reset(backend()->display()->compositor()->createSurface());
|
||||
Q_ASSERT(m_disableCount > 0);
|
||||
--m_disableCount;
|
||||
if (m_disableCount == 0) {
|
||||
install();
|
||||
}
|
||||
}
|
||||
|
||||
void WaylandCursor::init()
|
||||
void WaylandCursor::disable()
|
||||
{
|
||||
installImage();
|
||||
++m_disableCount;
|
||||
if (m_disableCount == 1) {
|
||||
uninstall();
|
||||
}
|
||||
}
|
||||
|
||||
void WaylandCursor::installImage()
|
||||
void WaylandCursor::install()
|
||||
{
|
||||
const QImage image = Cursors::self()->currentCursor()->image();
|
||||
if (image.isNull() || image.size().isEmpty()) {
|
||||
doInstallImage(nullptr, QSize(), 1);
|
||||
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();
|
||||
doInstallImage(imageBuffer, image.size(), image.devicePixelRatio());
|
||||
|
||||
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::doInstallImage(wl_buffer *image, const QSize &size, qreal scale)
|
||||
void WaylandCursor::uninstall()
|
||||
{
|
||||
auto *pointer = m_backend->seat()->pointerDevice()->nativePointer();
|
||||
if (!pointer || !pointer->isValid()) {
|
||||
return;
|
||||
}
|
||||
pointer->setCursor(m_surface.get(), image ? Cursors::self()->currentCursor()->hotspot() : QPoint());
|
||||
drawSurface(image, size, scale);
|
||||
}
|
||||
|
||||
void WaylandCursor::drawSurface(wl_buffer *image, const QSize &size, qreal scale)
|
||||
{
|
||||
m_surface->attachBuffer(image);
|
||||
m_surface->setScale(std::ceil(scale));
|
||||
m_surface->damageBuffer(QRect(QPoint(0, 0), size));
|
||||
m_surface->commit(KWayland::Client::Surface::CommitFlag::None);
|
||||
}
|
||||
|
||||
WaylandSubSurfaceCursor::WaylandSubSurfaceCursor(WaylandBackend *backend)
|
||||
: WaylandCursor(backend)
|
||||
{
|
||||
}
|
||||
|
||||
WaylandSubSurfaceCursor::~WaylandSubSurfaceCursor() = default;
|
||||
|
||||
void WaylandSubSurfaceCursor::init()
|
||||
{
|
||||
if (auto *pointer = backend()->seat()->pointerDevice()->nativePointer()) {
|
||||
pointer->hideCursor();
|
||||
}
|
||||
}
|
||||
|
||||
void WaylandSubSurfaceCursor::changeOutput(WaylandOutput *output)
|
||||
{
|
||||
m_subSurface.reset();
|
||||
m_output = output;
|
||||
if (!output) {
|
||||
return;
|
||||
}
|
||||
createSubSurface();
|
||||
surface()->commit();
|
||||
}
|
||||
|
||||
void WaylandSubSurfaceCursor::createSubSurface()
|
||||
{
|
||||
if (m_subSurface) {
|
||||
return;
|
||||
}
|
||||
if (!m_output) {
|
||||
return;
|
||||
}
|
||||
resetSurface();
|
||||
m_subSurface.reset(backend()->display()->subCompositor()->createSubSurface(surface(), m_output->surface()));
|
||||
m_subSurface->setMode(SubSurface::Mode::Desynchronized);
|
||||
}
|
||||
|
||||
void WaylandSubSurfaceCursor::doInstallImage(wl_buffer *image, const QSize &size, qreal scale)
|
||||
{
|
||||
if (!image) {
|
||||
m_subSurface.reset();
|
||||
return;
|
||||
}
|
||||
createSubSurface();
|
||||
// cursor position might have changed due to different cursor hot spot
|
||||
move(input()->pointer()->pos());
|
||||
drawSurface(image, size, scale);
|
||||
}
|
||||
|
||||
QPointF WaylandSubSurfaceCursor::absoluteToRelativePosition(const QPointF &position)
|
||||
{
|
||||
return position - m_output->geometry().topLeft() - Cursors::self()->currentCursor()->hotspot();
|
||||
}
|
||||
|
||||
void WaylandSubSurfaceCursor::move(const QPointF &globalPosition)
|
||||
{
|
||||
auto *output = backend()->getOutputAt(globalPosition.toPoint());
|
||||
if (!m_output || (output && m_output != output)) {
|
||||
changeOutput(output);
|
||||
if (!m_output) {
|
||||
// cursor might be off the grid
|
||||
return;
|
||||
}
|
||||
installImage();
|
||||
return;
|
||||
}
|
||||
if (!m_subSurface) {
|
||||
return;
|
||||
}
|
||||
// place the sub-surface relative to the output it is on and factor in the hotspot
|
||||
const auto relativePosition = globalPosition.toPoint() - Cursors::self()->currentCursor()->hotspot() - m_output->geometry().topLeft();
|
||||
m_subSurface->setPosition(relativePosition);
|
||||
m_output->renderLoop()->scheduleRepaint();
|
||||
pointer->hideCursor();
|
||||
}
|
||||
|
||||
WaylandInputDevice::WaylandInputDevice(KWayland::Client::Keyboard *keyboard, WaylandSeat *seat)
|
||||
|
@ -577,26 +506,16 @@ bool WaylandBackend::initialize()
|
|||
QObject::connect(dispatcher, &QAbstractEventDispatcher::awake, m_display.get(), &WaylandDisplay::flush);
|
||||
|
||||
connect(Cursors::self(), &Cursors::currentCursorChanged, this, [this]() {
|
||||
if (!m_seat || !m_waylandCursor) {
|
||||
return;
|
||||
}
|
||||
m_waylandCursor->installImage();
|
||||
});
|
||||
connect(Cursors::self(), &Cursors::positionChanged, this, [this](Cursor *cursor, const QPoint &position) {
|
||||
if (m_waylandCursor) {
|
||||
m_waylandCursor->move(position);
|
||||
}
|
||||
m_waylandCursor->install();
|
||||
});
|
||||
connect(this, &WaylandBackend::pointerLockChanged, this, [this](bool locked) {
|
||||
if (locked) {
|
||||
m_waylandCursor = std::make_unique<WaylandSubSurfaceCursor>(this);
|
||||
m_waylandCursor->move(input()->pointer()->pos());
|
||||
m_waylandCursor->disable();
|
||||
m_seat->createRelativePointer();
|
||||
} else {
|
||||
m_seat->destroyRelativePointer();
|
||||
m_waylandCursor = std::make_unique<WaylandCursor>(this);
|
||||
m_waylandCursor->enable();
|
||||
}
|
||||
m_waylandCursor->init();
|
||||
});
|
||||
|
||||
return true;
|
||||
|
|
|
@ -38,7 +38,6 @@ class PointerSwipeGesture;
|
|||
class PointerPinchGesture;
|
||||
class RelativePointer;
|
||||
class Seat;
|
||||
class SubSurface;
|
||||
class Surface;
|
||||
class Touch;
|
||||
}
|
||||
|
@ -61,52 +60,18 @@ class WaylandCursor
|
|||
{
|
||||
public:
|
||||
explicit WaylandCursor(WaylandBackend *backend);
|
||||
virtual ~WaylandCursor();
|
||||
~WaylandCursor();
|
||||
|
||||
virtual void init();
|
||||
virtual void move(const QPointF &globalPosition)
|
||||
{
|
||||
}
|
||||
void enable();
|
||||
void disable();
|
||||
|
||||
void installImage();
|
||||
|
||||
protected:
|
||||
void resetSurface();
|
||||
virtual void doInstallImage(wl_buffer *image, const QSize &size, qreal scale);
|
||||
void drawSurface(wl_buffer *image, const QSize &size, qreal scale);
|
||||
|
||||
KWayland::Client::Surface *surface() const
|
||||
{
|
||||
return m_surface.get();
|
||||
}
|
||||
WaylandBackend *backend() const
|
||||
{
|
||||
return m_backend;
|
||||
}
|
||||
void install();
|
||||
void uninstall();
|
||||
|
||||
private:
|
||||
WaylandBackend *const m_backend;
|
||||
std::unique_ptr<KWayland::Client::Surface> m_surface;
|
||||
};
|
||||
|
||||
class WaylandSubSurfaceCursor : public WaylandCursor
|
||||
{
|
||||
public:
|
||||
explicit WaylandSubSurfaceCursor(WaylandBackend *backend);
|
||||
~WaylandSubSurfaceCursor() override;
|
||||
|
||||
void init() override;
|
||||
|
||||
void move(const QPointF &globalPosition) override;
|
||||
|
||||
private:
|
||||
void changeOutput(WaylandOutput *output);
|
||||
void doInstallImage(wl_buffer *image, const QSize &size, qreal scale) override;
|
||||
void createSubSurface();
|
||||
|
||||
QPointF absoluteToRelativePosition(const QPointF &position);
|
||||
WaylandOutput *m_output = nullptr;
|
||||
std::unique_ptr<KWayland::Client::SubSurface> m_subSurface;
|
||||
int m_disableCount = 0;
|
||||
};
|
||||
|
||||
class WaylandInputDevice : public InputDevice
|
||||
|
|
|
@ -162,7 +162,6 @@ WaylandDisplay::~WaylandDisplay()
|
|||
m_seat.reset();
|
||||
m_xdgDecorationManager.reset();
|
||||
m_shmPool.reset();
|
||||
m_subCompositor.reset();
|
||||
m_xdgShell.reset();
|
||||
|
||||
if (m_registry) {
|
||||
|
@ -230,11 +229,6 @@ KWayland::Client::ShmPool *WaylandDisplay::shmPool() const
|
|||
return m_shmPool.get();
|
||||
}
|
||||
|
||||
KWayland::Client::SubCompositor *WaylandDisplay::subCompositor() const
|
||||
{
|
||||
return m_subCompositor.get();
|
||||
}
|
||||
|
||||
KWayland::Client::Seat *WaylandDisplay::seat() const
|
||||
{
|
||||
return m_seat.get();
|
||||
|
@ -260,9 +254,6 @@ void WaylandDisplay::registry_global(void *data, wl_registry *registry, uint32_t
|
|||
}
|
||||
display->m_compositor = std::make_unique<KWayland::Client::Compositor>();
|
||||
display->m_compositor->setup(static_cast<wl_compositor *>(wl_registry_bind(registry, name, &wl_compositor_interface, std::min(version, 4u))));
|
||||
} else if (strcmp(interface, wl_subcompositor_interface.name) == 0) {
|
||||
display->m_subCompositor = std::make_unique<KWayland::Client::SubCompositor>();
|
||||
display->m_subCompositor->setup(static_cast<wl_subcompositor *>(wl_registry_bind(registry, name, &wl_subcompositor_interface, std::min(version, 1u))));
|
||||
} else if (strcmp(interface, wl_shm_interface.name) == 0) {
|
||||
display->m_shmPool = std::make_unique<KWayland::Client::ShmPool>();
|
||||
display->m_shmPool->setup(static_cast<wl_shm *>(wl_registry_bind(registry, name, &wl_shm_interface, std::min(version, 1u))));
|
||||
|
|
|
@ -23,7 +23,6 @@ class PointerGestures;
|
|||
class RelativePointerManager;
|
||||
class Seat;
|
||||
class ShmPool;
|
||||
class SubCompositor;
|
||||
class XdgDecorationManager;
|
||||
class XdgShell;
|
||||
}
|
||||
|
@ -54,7 +53,6 @@ public:
|
|||
KWayland::Client::Seat *seat() const;
|
||||
KWayland::Client::XdgDecorationManager *xdgDecorationManager() const;
|
||||
KWayland::Client::ShmPool *shmPool() const;
|
||||
KWayland::Client::SubCompositor *subCompositor() const;
|
||||
KWayland::Client::XdgShell *xdgShell() const;
|
||||
|
||||
public Q_SLOTS:
|
||||
|
@ -74,7 +72,6 @@ private:
|
|||
std::unique_ptr<KWayland::Client::Seat> m_seat;
|
||||
std::unique_ptr<KWayland::Client::XdgDecorationManager> m_xdgDecorationManager;
|
||||
std::unique_ptr<KWayland::Client::ShmPool> m_shmPool;
|
||||
std::unique_ptr<KWayland::Client::SubCompositor> m_subCompositor;
|
||||
std::unique_ptr<KWayland::Client::XdgShell> m_xdgShell;
|
||||
};
|
||||
|
||||
|
|
|
@ -86,6 +86,11 @@ RenderLoop *WaylandOutput::renderLoop() const
|
|||
return m_renderLoop.get();
|
||||
}
|
||||
|
||||
bool WaylandOutput::usesSoftwareCursor() const
|
||||
{
|
||||
return m_hasPointerLock;
|
||||
}
|
||||
|
||||
void WaylandOutput::init(const QSize &pixelSize, qreal scale)
|
||||
{
|
||||
m_renderLoop->setRefreshRate(s_refreshRate);
|
||||
|
|
|
@ -40,6 +40,7 @@ public:
|
|||
~WaylandOutput() override;
|
||||
|
||||
RenderLoop *renderLoop() const override;
|
||||
bool usesSoftwareCursor() const override;
|
||||
|
||||
void init(const QSize &pixelSize, qreal scale);
|
||||
|
||||
|
|
Loading…
Reference in a new issue