Rework cursor image handling for Wayland

So far updating the cursor image was not really defined. It was possible
to use the cursor image from the wayland seat or have a custom set cursor
image. But there are no rules in place to decide which one to use when.

With this change a dedicated CursorImage class is introduced which tracks
the cursor image changes on the seat, on the decoration, in the effects
and so on. In addition it tracks which is the current source for the
image, that is whether e.g. the cursor from the seat or from effects
override should be used. Whenever the cursor image changes a signal is
emitted, which is connected to the signal in AbstractBackend.

Based on that the backends can directly show the image. The existing
code in the backends to install a cursor shape or to install the cursor
from the server is completely dropped. For the backend it's irrelevant
from where the image comes from.

A new feature added is that the cursor image is marked as rendered. This
is then passed on to the frame rendered in the Surface and thus animated
cursors are finally working. Unfortunately animated cursors are broken in
Qt (see https://bugreports.qt.io/browse/QTBUG-48181 ).
icc-effect-5.14.5
Martin Gräßlin 2016-02-23 12:29:05 +01:00
parent 5acf9abda8
commit a029300ce5
13 changed files with 425 additions and 226 deletions

View File

@ -23,18 +23,9 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
#include "composite.h"
#include "cursor.h"
#include "input.h"
#include "pointer_input.h"
#include "scene_opengl.h"
#include "wayland_server.h"
#include "wayland_cursor_theme.h"
// KWayland
#include <KWayland/Client/buffer.h>
#include <KWayland/Client/connection_thread.h>
#include <KWayland/Server/buffer_interface.h>
#include <KWayland/Server/clientconnection.h>
#include <KWayland/Server/seat_interface.h>
#include <KWayland/Server/surface_interface.h>
// Wayland
#include <wayland-cursor.h>
namespace KWin
{
@ -50,87 +41,14 @@ AbstractBackend::~AbstractBackend()
WaylandServer::self()->uninstallBackend(this);
}
void AbstractBackend::installCursorFromServer()
QImage AbstractBackend::softwareCursor() const
{
if (!m_softWareCursor) {
return;
}
triggerCursorRepaint();
updateCursorFromServer();
return input()->pointer()->cursorImage();
}
void AbstractBackend::updateCursorFromServer()
QPoint AbstractBackend::softwareCursorHotspot() const
{
if (!waylandServer() || !waylandServer()->seat()->focusedPointer()) {
return;
}
auto c = waylandServer()->seat()->focusedPointer()->cursor();
if (!c) {
return;
}
auto cursorSurface = c->surface();
if (cursorSurface.isNull()) {
return;
}
auto buffer = cursorSurface.data()->buffer();
if (!buffer) {
return;
}
m_cursor.hotspot = c->hotspot();
m_cursor.image = buffer->data().copy();
emit cursorChanged();
}
void AbstractBackend::installCursorImage(Qt::CursorShape shape)
{
if (!m_softWareCursor) {
return;
}
updateCursorImage(shape);
}
void AbstractBackend::updateCursorImage(Qt::CursorShape shape)
{
if (!m_cursorTheme) {
// check whether we can create it
if (waylandServer() && waylandServer()->internalShmPool()) {
m_cursorTheme = new WaylandCursorTheme(waylandServer()->internalShmPool(), this);
connect(waylandServer(), &WaylandServer::terminatingInternalClientConnection, this,
[this] {
delete m_cursorTheme;
m_cursorTheme = nullptr;
}
);
}
}
if (!m_cursorTheme) {
return;
}
wl_cursor_image *cursor = m_cursorTheme->get(shape);
if (!cursor) {
return;
}
wl_buffer *b = wl_cursor_image_get_buffer(cursor);
if (!b) {
return;
}
waylandServer()->internalClientConection()->flush();
waylandServer()->dispatch();
installThemeCursor(KWayland::Client::Buffer::getId(b), QPoint(cursor->hotspot_x, cursor->hotspot_y));
}
void AbstractBackend::installThemeCursor(quint32 id, const QPoint &hotspot)
{
auto buffer = KWayland::Server::BufferInterface::get(waylandServer()->internalConnection()->getResource(id));
if (!buffer) {
return;
}
if (m_softWareCursor) {
triggerCursorRepaint();
}
m_cursor.hotspot = hotspot;
m_cursor.image = buffer->data().copy();
emit cursorChanged();
return input()->pointer()->cursorHotSpot();
}
Screens *AbstractBackend::createScreens(QObject *parent)
@ -164,17 +82,24 @@ void AbstractBackend::setSoftWareCursor(bool set)
void AbstractBackend::triggerCursorRepaint()
{
if (!Compositor::self() || m_cursor.image.isNull()) {
if (!Compositor::self()) {
return;
}
Compositor::self()->addRepaint(m_cursor.lastRenderedPosition.x() - m_cursor.hotspot.x(),
m_cursor.lastRenderedPosition.y() - m_cursor.hotspot.y(),
m_cursor.image.width(), m_cursor.image.height());
const QPoint &hotSpot = softwareCursorHotspot();
const QSize &size = softwareCursor().size();
Compositor::self()->addRepaint(m_cursor.lastRenderedPosition.x() - hotSpot.x(),
m_cursor.lastRenderedPosition.y() - hotSpot.y(),
size.width(), size.height());
}
void AbstractBackend::markCursorAsRendered()
{
m_cursor.lastRenderedPosition = Cursor::pos();
if (m_softWareCursor) {
m_cursor.lastRenderedPosition = Cursor::pos();
}
if (input()->pointer()) {
input()->pointer()->markCursorAsRendered();
}
}
void AbstractBackend::keyboardKeyPressed(quint32 key, quint32 time)

View File

@ -40,8 +40,6 @@ public:
virtual ~AbstractBackend();
virtual void init() = 0;
virtual void installCursorFromServer();
virtual void installCursorImage(Qt::CursorShape shape);
virtual Screens *createScreens(QObject *parent = nullptr);
virtual OpenGLBackend *createOpenGLBackend();
virtual QPainterBackend *createQPainterBackend();
@ -78,12 +76,8 @@ public:
bool usesSoftwareCursor() const {
return m_softWareCursor;
}
QImage softwareCursor() const {
return m_cursor.image;
}
QPoint softwareCursorHotspot() const {
return m_cursor.hotspot;
}
QImage softwareCursor() const;
QPoint softwareCursorHotspot() const;
void markCursorAsRendered();
bool handlesOutputs() const {
@ -143,8 +137,6 @@ Q_SIGNALS:
protected:
explicit AbstractBackend(QObject *parent = nullptr);
void setSoftWareCursor(bool set);
void updateCursorFromServer();
void updateCursorImage(Qt::CursorShape shape);
void handleOutputs() {
m_handlesOutputs = true;
}
@ -162,14 +154,10 @@ protected:
private:
void triggerCursorRepaint();
void installThemeCursor(quint32 id, const QPoint &hotspot);
bool m_softWareCursor = false;
struct {
QPoint hotspot;
QImage image;
QPoint lastRenderedPosition;
} m_cursor;
WaylandCursorTheme *m_cursorTheme = nullptr;
bool m_handlesOutputs = false;
bool m_ready = false;
QSize m_initialWindowSize;

View File

@ -529,16 +529,6 @@ void DrmBackend::present(DrmBuffer *buffer, DrmOutput *output)
}
}
void DrmBackend::installCursorFromServer()
{
updateCursorFromServer();
}
void DrmBackend::installCursorImage(Qt::CursorShape shape)
{
updateCursorImage(shape);
}
void DrmBackend::initCursor()
{
uint64_t capability = 0;
@ -562,7 +552,6 @@ void DrmBackend::initCursor()
// now we have screens and can set cursors, so start tracking
connect(this, &DrmBackend::cursorChanged, this, &DrmBackend::updateCursor);
connect(Cursor::self(), &Cursor::posChanged, this, &DrmBackend::moveCursor);
installCursorImage(Qt::ArrowCursor);
}
void DrmBackend::setCursor()
@ -572,6 +561,7 @@ void DrmBackend::setCursor()
for (auto it = m_outputs.constBegin(); it != m_outputs.constEnd(); ++it) {
(*it)->showCursor(c);
}
markCursorAsRendered();
}
void DrmBackend::updateCursor()

View File

@ -71,8 +71,6 @@ public:
Screens *createScreens(QObject *parent = nullptr) override;
QPainterBackend *createQPainterBackend() override;
OpenGLBackend* createOpenGLBackend() override;
void installCursorFromServer() override;
void installCursorImage(Qt::CursorShape shape) override;
void init() override;
DrmBuffer *createBuffer(const QSize &size);

View File

@ -68,7 +68,6 @@ WaylandSeat::WaylandSeat(wl_seat *seat, WaylandBackend *backend)
, m_keyboard(NULL)
, m_touch(nullptr)
, m_cursor(NULL)
, m_theme(new WaylandCursorTheme(backend->shmPool(), this))
, m_enteredSerial(0)
, m_backend(backend)
, m_installCursor(false)
@ -244,21 +243,15 @@ void WaylandSeat::installCursorImage(wl_buffer *image, const QSize &size, const
m_cursor->attachBuffer(image);
m_cursor->damage(QRect(QPoint(0,0), size));
m_cursor->commit(Surface::CommitFlag::None);
}
void WaylandSeat::installCursorImage(Qt::CursorShape shape)
{
wl_cursor_image *image = m_theme->get(shape);
if (!image) {
return;
}
installCursorImage(wl_cursor_image_get_buffer(image),
QSize(image->width, image->height),
QPoint(image->hotspot_x, image->hotspot_y));
m_backend->flush();
}
void WaylandSeat::installCursorImage(const QImage &image, const QPoint &hotSpot)
{
if (image.isNull()) {
installCursorImage(nullptr, QSize(), QPoint());
return;
}
installCursorImage(*(m_backend->shmPool()->createBuffer(image).data()), image.size(), hotSpot);
}
@ -337,6 +330,15 @@ void WaylandBackend::init()
if (!deviceIdentifier().isEmpty()) {
m_connectionThreadObject->setSocketName(deviceIdentifier());
}
connect(this, &WaylandBackend::cursorChanged, this,
[this] {
if (m_seat.isNull() || !m_seat->isInstallCursor()) {
return;
}
m_seat->installCursorImage(softwareCursor(), softwareCursorHotspot());
markCursorAsRendered();
}
);
initConnection();
}
@ -389,35 +391,6 @@ void WaylandBackend::initConnection()
m_connectionThreadObject->initConnection();
}
void WaylandBackend::installCursorImage(Qt::CursorShape shape)
{
if (!m_seat.isNull() && m_seat->isInstallCursor()) {
m_seat->installCursorImage(shape);
}
}
void WaylandBackend::installCursorFromServer()
{
if (!waylandServer() || !waylandServer()->seat()->focusedPointer()) {
return;
}
auto c = waylandServer()->seat()->focusedPointer()->cursor();
if (c) {
auto cursorSurface = c->surface();
if (!cursorSurface.isNull()) {
auto buffer = cursorSurface.data()->buffer();
if (buffer) {
// set cursor
if (!m_seat.isNull() && m_seat->isInstallCursor()) {
m_seat->installCursorImage(buffer->data(), c->hotspot());
}
return;
}
}
}
// TODO: unset cursor
}
void WaylandBackend::createSurface()
{
m_surface = m_compositor->createSurface(this);
@ -478,6 +451,13 @@ QPainterBackend *WaylandBackend::createQPainterBackend()
return new WaylandQPainterBackend(this);
}
void WaylandBackend::flush()
{
if (m_connectionThreadObject) {
m_connectionThreadObject->flush();
}
}
}
} // KWin

View File

@ -74,7 +74,6 @@ public:
virtual ~WaylandSeat();
void installCursorImage(wl_buffer *image, const QSize &size, const QPoint &hotspot);
void installCursorImage(Qt::CursorShape shape);
void installCursorImage(const QImage &image, const QPoint &hotspot);
void setInstallCursor(bool install);
bool isInstallCursor() const {
@ -89,7 +88,6 @@ private:
KWayland::Client::Keyboard *m_keyboard;
KWayland::Client::Touch *m_touch;
KWayland::Client::Surface *m_cursor;
WaylandCursorTheme *m_theme = nullptr;
uint32_t m_enteredSerial;
WaylandBackend *m_backend;
bool m_installCursor;
@ -116,8 +114,6 @@ public:
KWayland::Client::Surface *surface() const;
QSize shellSurfaceSize() const;
void installCursorImage(Qt::CursorShape shape) override;
void installCursorFromServer() override;
Screens *createScreens(QObject *parent = nullptr) override;
OpenGLBackend *createOpenGLBackend() override;
@ -127,6 +123,8 @@ public:
return shellSurfaceSize();
}
void flush();
Q_SIGNALS:
void shellSurfaceSizeChanged(const QSize &size);
void systemCompositorDied();

View File

@ -93,6 +93,11 @@ void X11WindowedBackend::init()
XRenderUtils::init(m_connection, m_screen->root);
createWindow();
startEventReading();
connect(this, &X11WindowedBackend::cursorChanged, this,
[this] {
createCursor(softwareCursor(), softwareCursorHotspot());
}
);
setReady(true);
waylandServer()->seat()->setHasPointer(true);
waylandServer()->seat()->setHasKeyboard(true);
@ -374,25 +379,6 @@ void X11WindowedBackend::updateSize(xcb_configure_notify_event_t *event)
}
}
void X11WindowedBackend::installCursorFromServer()
{
if (!waylandServer() || !waylandServer()->seat()->focusedPointer()) {
return;
}
auto c = waylandServer()->seat()->focusedPointer()->cursor();
if (c) {
auto cursorSurface = c->surface();
if (!cursorSurface.isNull()) {
auto buffer = cursorSurface.data()->buffer();
if (buffer) {
createCursor(buffer->data(), c->hotspot());
return;
}
}
}
// TODO: unset cursor
}
void X11WindowedBackend::createCursor(const QImage &img, const QPoint &hotspot)
{
const xcb_pixmap_t pix = xcb_generate_id(m_connection);
@ -417,13 +403,7 @@ void X11WindowedBackend::createCursor(const QImage &img, const QPoint &hotspot)
}
m_cursor = cid;
xcb_flush(m_connection);
}
void X11WindowedBackend::installCursorImage(Qt::CursorShape shape)
{
// TODO: only update if shape changed
updateCursorImage(shape);
createCursor(softwareCursor(), softwareCursorHotspot());
markCursorAsRendered();
}
xcb_window_t X11WindowedBackend::rootWindow() const

View File

@ -63,9 +63,6 @@ public:
}
xcb_window_t rootWindow() const;
void installCursorFromServer() override;
void installCursorImage(Qt::CursorShape shape) override;
Screens *createScreens(QObject *parent = nullptr) override;
OpenGLBackend *createOpenGLBackend() override;
QPainterBackend* createQPainterBackend() override;

View File

@ -30,6 +30,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
#include "client.h"
#include "cursor.h"
#include "group.h"
#include "pointer_input.h"
#include "scene_xrender.h"
#include "scene_qpainter.h"
#include "unmanaged.h"
@ -709,9 +710,7 @@ void EffectsHandlerImpl::startMouseInterception(Effect *effect, Qt::CursorShape
return;
}
if (kwinApp()->operationMode() != Application::OperationModeX11) {
if (AbstractBackend *w = waylandServer()->backend()) {
w->installCursorImage(shape);
}
input()->pointer()->setEffectsOverrideCursor(shape);
return;
}
// NOTE: it is intended to not perform an XPointerGrab on X11. See documentation in kwineffects.h
@ -743,6 +742,7 @@ void EffectsHandlerImpl::stopMouseInterception(Effect *effect)
}
m_grabbedMouseEffects.removeAll(effect);
if (kwinApp()->operationMode() != Application::OperationModeX11) {
input()->pointer()->removeEffectsOverrideCursor();
return;
}
if (m_grabbedMouseEffects.isEmpty()) {
@ -751,6 +751,11 @@ void EffectsHandlerImpl::stopMouseInterception(Effect *effect)
}
}
bool EffectsHandlerImpl::isMouseInterception() const
{
return m_grabbedMouseEffects.count() > 0;
}
void EffectsHandlerImpl::registerGlobalShortcut(const QKeySequence &shortcut, QAction *action)
{
input()->registerShortcut(shortcut, action);
@ -1225,11 +1230,7 @@ QSize EffectsHandlerImpl::virtualScreenSize() const
void EffectsHandlerImpl::defineCursor(Qt::CursorShape shape)
{
if (!m_mouseInterceptionWindow.isValid()) {
if (waylandServer()) {
if (AbstractBackend *w = waylandServer()->backend()) {
w->installCursorImage(shape);
}
}
input()->pointer()->setEffectsOverrideCursor(shape);
return;
}
const xcb_cursor_t c = Cursor::x11Cursor(shape);

View File

@ -123,6 +123,7 @@ public:
// not performing XGrabPointer
void startMouseInterception(Effect *effect, Qt::CursorShape shape) override;
void stopMouseInterception(Effect *effect) override;
bool isMouseInterception() const;
void registerGlobalShortcut(const QKeySequence &shortcut, QAction *action) override;
void registerPointerShortcut(Qt::KeyboardModifiers modifiers, Qt::MouseButton pointerButtons, QAction *action) override;
void registerAxisShortcut(Qt::KeyboardModifiers modifiers, PointerAxisDirection axis, QAction *action) override;

View File

@ -443,7 +443,6 @@ public:
if (event->type() == QEvent::MouseButtonRelease) {
decoration->client()->processDecorationButtonRelease(&e);
}
input()->pointer()->installCursorFromDecoration();
return true;
}
default:

View File

@ -19,20 +19,29 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
*********************************************************************/
#include "pointer_input.h"
#include "abstract_backend.h"
#include "effects.h"
#include "screens.h"
#include "shell_client.h"
#include "wayland_cursor_theme.h"
#include "wayland_server.h"
#include "workspace.h"
#include "decorations/decoratedclient.h"
// KDecoration
#include <KDecoration2/Decoration>
// KWayland
#include <KWayland/Client/connection_thread.h>
#include <KWayland/Client/buffer.h>
#include <KWayland/Server/buffer_interface.h>
#include <KWayland/Server/display.h>
#include <KWayland/Server/seat_interface.h>
#include <KWayland/Server/surface_interface.h>
// screenlocker
#include <KScreenLocker/KsldApp>
#include <QHoverEvent>
#include <QWindow>
// Wayland
#include <wayland-cursor.h>
#include <linux/input.h>
@ -100,6 +109,7 @@ static bool screenContainsPos(const QPointF &pos)
PointerInputRedirection::PointerInputRedirection(InputRedirection* parent)
: QObject(parent)
, m_input(parent)
, m_cursor(nullptr)
, m_supportsWarping(Application::usesLibinput())
{
}
@ -109,7 +119,10 @@ PointerInputRedirection::~PointerInputRedirection() = default;
void PointerInputRedirection::init()
{
Q_ASSERT(!m_inited);
m_cursor = new CursorImage(this);
m_inited = true;
connect(m_cursor, &CursorImage::changed, waylandServer()->backend(), &AbstractBackend::cursorChanged);
emit m_cursor->changed();
connect(workspace(), &Workspace::stackingOrderChanged, this, &PointerInputRedirection::update);
connect(screens(), &Screens::changed, this, &PointerInputRedirection::updateAfterScreenChange);
connect(ScreenLocker::KSldApp::self(), &ScreenLocker::KSldApp::lockStateChanged, this, &PointerInputRedirection::update);
@ -219,6 +232,9 @@ void PointerInputRedirection::update()
if (m_decoration || m_internalWindow) {
t = nullptr;
}
if (m_decoration != oldDeco) {
emit decorationChanged();
}
auto oldWindow = m_window;
if (!oldWindow.isNull() && t == m_window.data()) {
return;
@ -231,11 +247,6 @@ void PointerInputRedirection::update()
}
disconnect(m_windowGeometryConnection);
m_windowGeometryConnection = QMetaObject::Connection();
if (auto p = seat->focusedPointer()) {
if (auto c = p->cursor()) {
disconnect(c, &KWayland::Server::Cursor::changed, waylandServer()->backend(), &AbstractBackend::installCursorFromServer);
}
}
}
if (AbstractClient *c = qobject_cast<AbstractClient*>(t)) {
// only send enter if it wasn't on deco for the same client before
@ -263,12 +274,6 @@ void PointerInputRedirection::update()
seat->setFocusedPointerSurfaceTransformation(m_window.data()->inputTransformation());
}
);
waylandServer()->backend()->installCursorFromServer();
if (auto p = seat->focusedPointer()) {
if (auto c = p->cursor()) {
connect(c, &KWayland::Server::Cursor::changed, waylandServer()->backend(), &AbstractBackend::installCursorFromServer);
}
}
} else {
seat->setFocusedPointerSurface(nullptr);
t = nullptr;
@ -372,9 +377,6 @@ void PointerInputRedirection::updateDecoration(Toplevel *t)
// send leave
QHoverEvent event(QEvent::HoverLeave, QPointF(), QPointF());
QCoreApplication::instance()->sendEvent(oldDeco->decoration(), &event);
if (!m_decoration) {
waylandServer()->backend()->installCursorImage(Qt::ArrowCursor);
}
}
if (m_decoration) {
if (m_decoration->client() != oldWindow) {
@ -385,7 +387,6 @@ void PointerInputRedirection::updateDecoration(Toplevel *t)
QHoverEvent event(QEvent::HoverMove, p, p);
QCoreApplication::instance()->sendEvent(m_decoration->decoration(), &event);
m_decoration->client()->processDecorationMove();
installCursorFromDecoration();
}
}
@ -445,14 +446,6 @@ bool PointerInputRedirection::supportsWarping() const
return false;
}
void PointerInputRedirection::installCursorFromDecoration()
{
if (!m_inited || !m_decoration) {
return;
}
waylandServer()->backend()->installCursorImage(m_decoration->client()->cursor());
}
void PointerInputRedirection::updateAfterScreenChange()
{
if (!m_inited) {
@ -468,4 +461,286 @@ void PointerInputRedirection::updateAfterScreenChange()
processMotion(pos, waylandServer()->seat()->timestamp());
}
QImage PointerInputRedirection::cursorImage() const
{
if (!m_inited) {
return QImage();
}
return m_cursor->image();
}
QPoint PointerInputRedirection::cursorHotSpot() const
{
if (!m_inited) {
return QPoint();
}
return m_cursor->hotSpot();
}
void PointerInputRedirection::markCursorAsRendered()
{
if (!m_inited) {
return;
}
m_cursor->markAsRendered();
}
void PointerInputRedirection::setEffectsOverrideCursor(Qt::CursorShape shape)
{
if (!m_inited) {
return;
}
m_cursor->setEffectsOverrideCursor(shape);
}
void PointerInputRedirection::removeEffectsOverrideCursor()
{
if (!m_inited) {
return;
}
m_cursor->removeEffectsOverrideCursor();
}
CursorImage::CursorImage(PointerInputRedirection *parent)
: QObject(parent)
, m_pointer(parent)
{
connect(waylandServer()->seat(), &KWayland::Server::SeatInterface::focusedPointerChanged, this, &CursorImage::update);
connect(ScreenLocker::KSldApp::self(), &ScreenLocker::KSldApp::lockStateChanged, this, &CursorImage::reevaluteSource);
connect(m_pointer, &PointerInputRedirection::decorationChanged, this, &CursorImage::updateDecoration);
loadThemeCursor(Qt::ArrowCursor, &m_fallbackCursor);
m_surfaceRenderedTimer.start();
}
CursorImage::~CursorImage() = default;
void CursorImage::markAsRendered()
{
if (m_currentSource != CursorSource::LockScreen && m_currentSource != CursorSource::PointerSurface) {
return;
}
auto p = waylandServer()->seat()->focusedPointer();
if (!p) {
return;
}
auto c = p->cursor();
if (!c) {
return;
}
auto cursorSurface = c->surface();
if (cursorSurface.isNull()) {
return;
}
cursorSurface->frameRendered(m_surfaceRenderedTimer.elapsed());
}
void CursorImage::update()
{
using namespace KWayland::Server;
disconnect(m_serverCursor.connection);
auto p = waylandServer()->seat()->focusedPointer();
if (p) {
m_serverCursor.connection = connect(p, &PointerInterface::cursorChanged, this, &CursorImage::updateServerCursor);
} else {
m_serverCursor.connection = QMetaObject::Connection();
}
updateServerCursor();
}
void CursorImage::updateDecoration()
{
disconnect(m_decorationConnection);
auto deco = m_pointer->decoration();
AbstractClient *c = deco.isNull() ? nullptr : deco->client();
if (c) {
m_decorationConnection = connect(c, &AbstractClient::moveResizeCursorChanged, this, &CursorImage::updateDecorationCursor);
} else {
m_decorationConnection = QMetaObject::Connection();
}
updateDecorationCursor();
}
void CursorImage::updateDecorationCursor()
{
m_decorationCursor.image = QImage();
m_decorationCursor.hotSpot = QPoint();
auto deco = m_pointer->decoration();
if (AbstractClient *c = deco.isNull() ? nullptr : deco->client()) {
loadThemeCursor(c->cursor(), &m_decorationCursor);
if (m_currentSource == CursorSource::Decoration) {
emit changed();
}
}
reevaluteSource();
}
void CursorImage::updateServerCursor()
{
m_serverCursor.image = QImage();
m_serverCursor.hotSpot = QPoint();
reevaluteSource();
const bool needsEmit = m_currentSource == CursorSource::LockScreen || m_currentSource == CursorSource::PointerSurface;
auto p = waylandServer()->seat()->focusedPointer();
if (!p) {
if (needsEmit) {
emit changed();
}
return;
}
auto c = p->cursor();
if (!c) {
if (needsEmit) {
emit changed();
}
return;
}
auto cursorSurface = c->surface();
if (cursorSurface.isNull()) {
if (needsEmit) {
emit changed();
}
return;
}
auto buffer = cursorSurface.data()->buffer();
if (!buffer) {
if (needsEmit) {
emit changed();
}
return;
}
m_serverCursor.hotSpot = c->hotspot();
m_serverCursor.image = buffer->data().copy();
if (needsEmit) {
emit changed();
}
}
void CursorImage::loadTheme()
{
if (m_cursorTheme) {
return;
}
// check whether we can create it
if (waylandServer()->internalShmPool()) {
m_cursorTheme = new WaylandCursorTheme(waylandServer()->internalShmPool(), this);
connect(waylandServer(), &WaylandServer::terminatingInternalClientConnection, this,
[this] {
delete m_cursorTheme;
m_cursorTheme = nullptr;
}
);
}
}
void CursorImage::setEffectsOverrideCursor(Qt::CursorShape shape)
{
loadThemeCursor(shape, &m_effectsCursor);
if (m_currentSource == CursorSource::EffectsOverride) {
emit changed();
}
reevaluteSource();
}
void CursorImage::removeEffectsOverrideCursor()
{
reevaluteSource();
}
void CursorImage::loadThemeCursor(Qt::CursorShape shape, Image *image)
{
loadTheme();
if (!m_cursorTheme) {
return;
}
auto it = m_cursors.constFind(shape);
if (it == m_cursors.constEnd()) {
image->image = QImage();
image->hotSpot = QPoint();
wl_cursor_image *cursor = m_cursorTheme->get(shape);
if (!cursor) {
return;
}
wl_buffer *b = wl_cursor_image_get_buffer(cursor);
if (!b) {
return;
}
waylandServer()->internalClientConection()->flush();
waylandServer()->dispatch();
auto buffer = KWayland::Server::BufferInterface::get(waylandServer()->internalConnection()->getResource(KWayland::Client::Buffer::getId(b)));
if (!buffer) {
return;
}
it = decltype(it)(m_cursors.insert(shape, {buffer->data().copy(), QPoint(cursor->hotspot_x, cursor->hotspot_y)}));
}
image->hotSpot = it.value().hotSpot;
image->image = it.value().image;
}
void CursorImage::reevaluteSource()
{
if (waylandServer()->isScreenLocked()) {
setSource(CursorSource::LockScreen);
return;
}
if (effects && static_cast<EffectsHandlerImpl*>(effects)->isMouseInterception()) {
setSource(CursorSource::EffectsOverride);
return;
}
if (!m_pointer->decoration().isNull()) {
setSource(CursorSource::Decoration);
return;
}
if (!m_pointer->window().isNull()) {
setSource(CursorSource::PointerSurface);
return;
}
setSource(CursorSource::Fallback);
}
void CursorImage::setSource(CursorSource source)
{
if (m_currentSource == source) {
return;
}
m_currentSource = source;
emit changed();
}
QImage CursorImage::image() const
{
switch (m_currentSource) {
case CursorSource::EffectsOverride:
return m_effectsCursor.image;
case CursorSource::LockScreen:
case CursorSource::PointerSurface:
// lockscreen also uses server cursor image
return m_serverCursor.image;
case CursorSource::Decoration:
return m_decorationCursor.image;
case CursorSource::Fallback:
return m_fallbackCursor.image;
default:
Q_UNREACHABLE();
}
}
QPoint CursorImage::hotSpot() const
{
switch (m_currentSource) {
case CursorSource::EffectsOverride:
return m_effectsCursor.hotSpot;
case CursorSource::LockScreen:
case CursorSource::PointerSurface:
// lockscreen also uses server cursor image
return m_serverCursor.hotSpot;
case CursorSource::Decoration:
return m_decorationCursor.hotSpot;
case CursorSource::Fallback:
return m_fallbackCursor.hotSpot;
default:
Q_UNREACHABLE();
}
}
}

View File

@ -22,6 +22,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
#include "input.h"
#include <QElapsedTimer>
#include <QObject>
#include <QPointer>
#include <QPointF>
@ -31,8 +32,10 @@ class QWindow;
namespace KWin
{
class CursorImage;
class InputRedirection;
class Toplevel;
class WaylandCursorTheme;
namespace Decoration
{
@ -69,7 +72,11 @@ public:
return m_internalWindow;
}
void installCursorFromDecoration();
QImage cursorImage() const;
QPoint cursorHotSpot() const;
void markCursorAsRendered();
void setEffectsOverrideCursor(Qt::CursorShape shape);
void removeEffectsOverrideCursor();
/**
* @internal
@ -84,12 +91,16 @@ public:
*/
void processAxis(InputRedirection::PointerAxis axis, qreal delta, uint32_t time);
Q_SIGNALS:
void decorationChanged();
private:
void updatePosition(const QPointF &pos);
void updateButton(uint32_t button, InputRedirection::PointerButtonState state);
void updateInternalWindow();
void updateDecoration(Toplevel *t);
InputRedirection *m_input;
CursorImage *m_cursor;
bool m_inited = false;
bool m_supportsWarping;
QPointF m_pos;
@ -109,6 +120,62 @@ private:
QMetaObject::Connection m_internalWindowConnection;
};
class CursorImage : public QObject
{
Q_OBJECT
public:
explicit CursorImage(PointerInputRedirection *parent = nullptr);
virtual ~CursorImage();
void setEffectsOverrideCursor(Qt::CursorShape shape);
void removeEffectsOverrideCursor();
QImage image() const;
QPoint hotSpot() const;
void markAsRendered();
Q_SIGNALS:
void changed();
private:
void reevaluteSource();
void update();
void updateServerCursor();
void updateDecoration();
void updateDecorationCursor();
void loadTheme();
struct Image {
QImage image;
QPoint hotSpot;
};
void loadThemeCursor(Qt::CursorShape shape, Image *image);
enum class CursorSource {
LockScreen,
EffectsOverride,
PointerSurface,
Decoration,
Fallback
};
void setSource(CursorSource source);
PointerInputRedirection *m_pointer;
CursorSource m_currentSource = CursorSource::Fallback;
WaylandCursorTheme *m_cursorTheme = nullptr;
struct {
QMetaObject::Connection connection;
QImage image;
QPoint hotSpot;
} m_serverCursor;
Image m_effectsCursor;
Image m_decorationCursor;
QMetaObject::Connection m_decorationConnection;
Image m_fallbackCursor;
QHash<Qt::CursorShape, Image> m_cursors;
QElapsedTimer m_surfaceRenderedTimer;
};
}
#endif