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:
parent
2fec4d55fc
commit
1829345a2f
2 changed files with 195 additions and 21 deletions
|
@ -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);
|
||||
|
|
|
@ -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()
|
||||
{
|
||||
|
|
Loading…
Reference in a new issue