Support setting cursor image as a SubSurface

The idea is to manage the cursor position by ourself. This is needed
when KWin gains libinput support and doesn't rely on the Seat anymore.
The suggestion for this is to use SubSurfaces. The nice side-effect is
that we can do cursor warping again which we need e.g. for ScreenEdge
activation or for the kill helper, etc. Clients on the other side
still cannot (and should not) warp the pointer.
This commit is contained in:
Martin Gräßlin 2014-10-16 16:39:49 +02:00
parent 2fec4d55fc
commit 1829345a2f
2 changed files with 195 additions and 21 deletions

View file

@ -30,10 +30,13 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
#include <KWayland/Client/keyboard.h>
#include <KWayland/Client/output.h>
#include <KWayland/Client/pointer.h>
#include <KWayland/Client/region.h>
#include <KWayland/Client/registry.h>
#include <KWayland/Client/seat.h>
#include <KWayland/Client/shell.h>
#include <KWayland/Client/shm_pool.h>
#include <KWayland/Client/subcompositor.h>
#include <KWayland/Client/subsurface.h>
#include <KWayland/Client/surface.h>
// Qt
#include <QAbstractEventDispatcher>
@ -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>(surface), QPointer<Surface>(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<Surface> 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<Surface> 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<Surface> 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);

View file

@ -29,6 +29,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
#include <QSize>
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<KWayland::Client::Output*> &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()
{