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
parent
5acf9abda8
commit
a029300ce5
|
@ -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)
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
|
17
effects.cpp
17
effects.cpp
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -443,7 +443,6 @@ public:
|
|||
if (event->type() == QEvent::MouseButtonRelease) {
|
||||
decoration->client()->processDecorationButtonRelease(&e);
|
||||
}
|
||||
input()->pointer()->installCursorFromDecoration();
|
||||
return true;
|
||||
}
|
||||
default:
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in New Issue