diff --git a/src/backends/drm/drm_gpu.cpp b/src/backends/drm/drm_gpu.cpp index 5e83d36b36..9a5916ad0d 100644 --- a/src/backends/drm/drm_gpu.cpp +++ b/src/backends/drm/drm_gpu.cpp @@ -780,9 +780,6 @@ void DrmGpu::recreateSurfaces() for (const auto &output : std::as_const(m_virtualOutputs)) { output->recreateSurface(); } - for (const auto &output : std::as_const(m_drmOutputs)) { - output->updateCursor(); - } } DrmLease::DrmLease(DrmGpu *gpu, FileDescriptor &&fd, uint32_t lesseeId, const QVector &outputs) diff --git a/src/backends/drm/drm_output.cpp b/src/backends/drm/drm_output.cpp index 9801e538cc..14918d828f 100644 --- a/src/backends/drm/drm_output.cpp +++ b/src/backends/drm/drm_output.cpp @@ -18,7 +18,6 @@ #include "core/renderloop.h" #include "core/renderloop_p.h" #include "core/session.h" -#include "cursor.h" #include "drm_dumb_buffer.h" #include "drm_dumb_swapchain.h" #include "drm_egl_backend.h" @@ -95,12 +94,6 @@ DrmOutput::DrmOutput(DrmPipeline *pipeline) connect(&m_turnOffTimer, &QTimer::timeout, this, [this] { setDrmDpmsMode(DpmsMode::Off); }); - - if (!conn->isNonDesktop()) { - connect(Cursors::self(), &Cursors::currentCursorChanged, this, &DrmOutput::updateCursor); - connect(Cursors::self(), &Cursors::hiddenChanged, this, &DrmOutput::updateCursor); - connect(Cursors::self(), &Cursors::positionChanged, this, &DrmOutput::moveCursor); - } } DrmOutput::~DrmOutput() @@ -139,35 +132,38 @@ DrmLease *DrmOutput::lease() const return m_lease; } -void DrmOutput::updateCursor() +bool DrmOutput::setCursor(const QImage &image, const QPoint &hotspot) { static bool valid; static const bool forceSoftwareCursor = qEnvironmentVariableIntValue("KWIN_FORCE_SW_CURSOR", &valid) == 1 && valid; // hardware cursors are broken with the NVidia proprietary driver if (forceSoftwareCursor || (!valid && m_gpu->isNVidia())) { m_setCursorSuccessful = false; - return; + return false; } const auto layer = m_pipeline->cursorLayer(); if (!m_pipeline->crtc() || !layer) { - return; + return false; } - const Cursor *cursor = Cursors::self()->currentCursor(); - if (!cursor || cursor->image().isNull() || Cursors::self()->isCursorHidden()) { + m_cursor.image = image; + m_cursor.hotspot = hotspot; + if (m_cursor.image.isNull()) { if (layer->isVisible()) { layer->setVisible(false); m_pipeline->setCursor(); } - return; + return true; } bool rendered = false; - const QMatrix4x4 monitorMatrix = logicalToNativeMatrix(geometry(), scale(), transform()); - const QRect cursorRect = monitorMatrix.mapRect(cursor->geometry()); - if (cursorRect.width() <= m_gpu->cursorSize().width() && cursorRect.height() <= m_gpu->cursorSize().height()) { + const QMatrix4x4 monitorMatrix = logicalToNativeMatrix(rect(), scale(), transform()); + const QSize cursorSize = m_cursor.image.size() / m_cursor.image.devicePixelRatio(); + const QRect cursorRect = QRect(m_cursor.position, cursorSize); + const QRect nativeCursorRect = monitorMatrix.mapRect(cursorRect); + if (nativeCursorRect.width() <= m_gpu->cursorSize().width() && nativeCursorRect.height() <= m_gpu->cursorSize().height()) { if (const auto beginInfo = layer->beginFrame()) { const auto &[renderTarget, repaint] = beginInfo.value(); if (dynamic_cast(m_gpu->platform()->renderBackend())) { - renderCursorOpengl(renderTarget, cursor->geometry().size() * scale()); + renderCursorOpengl(renderTarget, cursorSize * scale()); } else { renderCursorQPainter(renderTarget); } @@ -180,43 +176,49 @@ void DrmOutput::updateCursor() m_pipeline->setCursor(); } m_setCursorSuccessful = false; - return; + return false; } - const QSize surfaceSize = m_gpu->cursorSize() / scale(); - const QRect layerRect = monitorMatrix.mapRect(QRect(cursor->geometry().topLeft(), surfaceSize)); - layer->setPosition(layerRect.topLeft()); - layer->setVisible(cursor->geometry().intersects(geometry())); + const QSize layerSize = m_gpu->cursorSize() / scale(); + const QRect layerRect = monitorMatrix.mapRect(QRect(m_cursor.position, layerSize)); + layer->setVisible(cursorRect.intersects(rect())); if (layer->isVisible()) { - m_setCursorSuccessful = m_pipeline->setCursor(logicalToNativeMatrix(QRect(QPoint(), layerRect.size()), scale(), transform()).map(cursor->hotspot())); + m_setCursorSuccessful = m_pipeline->setCursor(logicalToNativeMatrix(QRect(QPoint(), layerRect.size()), scale(), transform()).map(m_cursor.hotspot)); layer->setVisible(m_setCursorSuccessful); } + return m_setCursorSuccessful; } -void DrmOutput::moveCursor() +bool DrmOutput::moveCursor(const QPoint &position) { if (!m_setCursorSuccessful || !m_pipeline->crtc()) { - return; + return false; } - const auto layer = m_pipeline->cursorLayer(); - Cursor *cursor = Cursors::self()->currentCursor(); - if (!cursor || cursor->image().isNull() || Cursors::self()->isCursorHidden() || !cursor->geometry().intersects(geometry())) { + m_cursor.position = position; + + const QSize cursorSize = m_cursor.image.size() / m_cursor.image.devicePixelRatio(); + const QRect cursorRect = QRect(m_cursor.position, cursorSize); + + if (m_cursor.image.isNull() || !cursorRect.intersects(rect())) { + const auto layer = m_pipeline->cursorLayer(); if (layer->isVisible()) { layer->setVisible(false); m_pipeline->setCursor(); } - return; + return true; } - const QMatrix4x4 monitorMatrix = logicalToNativeMatrix(geometry(), scale(), transform()); - const QSize surfaceSize = m_gpu->cursorSize() / scale(); - const QRect cursorRect = monitorMatrix.mapRect(QRect(cursor->geometry().topLeft(), surfaceSize)); + const QMatrix4x4 monitorMatrix = logicalToNativeMatrix(rect(), scale(), transform()); + const QSize layerSize = m_gpu->cursorSize() / scale(); + const QRect layerRect = monitorMatrix.mapRect(QRect(m_cursor.position, layerSize)); + const auto layer = m_pipeline->cursorLayer(); layer->setVisible(true); - layer->setPosition(cursorRect.topLeft()); + layer->setPosition(layerRect.topLeft()); m_moveCursorSuccessful = m_pipeline->moveCursor(); layer->setVisible(m_moveCursorSuccessful); if (!m_moveCursorSuccessful) { m_pipeline->setCursor(); } + return m_moveCursorSuccessful; } QList> DrmOutput::getModes() const @@ -439,8 +441,6 @@ void DrmOutput::applyQueuedChanges(const OutputConfiguration &config) if (isEnabled() && dpmsMode() == DpmsMode::On) { m_gpu->platform()->turnOutputsOn(); } - - updateCursor(); } void DrmOutput::revertQueuedChanges() @@ -448,11 +448,6 @@ void DrmOutput::revertQueuedChanges() m_pipeline->revertPendingChanges(); } -bool DrmOutput::usesSoftwareCursor() const -{ - return !m_setCursorSuccessful || !m_moveCursorSuccessful; -} - DrmOutputLayer *DrmOutput::primaryLayer() const { return m_pipeline->primaryLayer(); @@ -472,28 +467,22 @@ void DrmOutput::setColorTransformation(const std::shared_ptrcurrentCursor()->image(); - if (img.isNull()) { - m_cursorTextureDirty = false; - return; + if (m_cursor.image.isNull()) { + m_cursor.texture.reset(); + m_cursor.cacheKey = 0; + } else { + m_cursor.texture = std::make_unique(m_cursor.image); + m_cursor.texture->setWrapMode(GL_CLAMP_TO_EDGE); + m_cursor.cacheKey = m_cursor.image.cacheKey(); } - m_cursorTexture = std::make_unique(img); - m_cursorTexture->setWrapMode(GL_CLAMP_TO_EDGE); - m_cursorTextureDirty = false; }; - if (!m_cursorTexture) { + if (!m_cursor.texture) { allocateTexture(); - - // handle shape update on case cursor image changed - connect(Cursors::self(), &Cursors::currentCursorChanged, this, [this]() { - m_cursorTextureDirty = true; - }); - } else if (m_cursorTextureDirty) { - const QImage image = Cursors::self()->currentCursor()->image(); - if (image.size() == m_cursorTexture->size()) { - m_cursorTexture->update(image); - m_cursorTextureDirty = false; + } else if (m_cursor.cacheKey != m_cursor.image.cacheKey()) { + if (m_cursor.image.size() == m_cursor.texture->size()) { + m_cursor.texture->update(m_cursor.image); + m_cursor.cacheKey = m_cursor.image.cacheKey(); } else { allocateTexture(); } @@ -508,18 +497,17 @@ void DrmOutput::renderCursorOpengl(const RenderTarget &renderTarget, const QSize glEnable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - m_cursorTexture->bind(); + m_cursor.texture->bind(); ShaderBinder binder(ShaderTrait::MapTexture); binder.shader()->setUniform(GLShader::ModelViewProjectionMatrix, mvp); - m_cursorTexture->render(QRect(0, 0, cursorSize.width(), cursorSize.height()), renderTarget.devicePixelRatio()); - m_cursorTexture->unbind(); + m_cursor.texture->render(QRect(0, 0, cursorSize.width(), cursorSize.height()), renderTarget.devicePixelRatio()); + m_cursor.texture->unbind(); glDisable(GL_BLEND); } void DrmOutput::renderCursorQPainter(const RenderTarget &renderTarget) { - const Cursor *cursor = Cursors::self()->currentCursor(); - const QImage cursorImage = cursor->image(); + const QRect cursorRect(QPoint(0, 0), m_cursor.image.size() / m_cursor.image.devicePixelRatio()); QImage *c = std::get(renderTarget.nativeHandle()); c->setDevicePixelRatio(scale()); @@ -527,9 +515,9 @@ void DrmOutput::renderCursorQPainter(const RenderTarget &renderTarget) QPainter p; p.begin(c); - p.setWorldTransform(logicalToNativeMatrix(cursor->rect(), 1, transform()).toTransform()); + p.setWorldTransform(logicalToNativeMatrix(cursorRect, 1, transform()).toTransform()); p.setRenderHint(QPainter::SmoothPixmapTransform); - p.drawImage(QPoint(0, 0), cursorImage); + p.drawImage(QPoint(0, 0), m_cursor.image); p.end(); } } diff --git a/src/backends/drm/drm_output.h b/src/backends/drm/drm_output.h index ffb6e16fbc..015368c0f8 100644 --- a/src/backends/drm/drm_output.h +++ b/src/backends/drm/drm_output.h @@ -50,9 +50,8 @@ public: void updateModes(); void updateDpmsMode(DpmsMode dpmsMode); - bool usesSoftwareCursor() const override; - void updateCursor(); - void moveCursor(); + bool setCursor(const QImage &image, const QPoint &hotspot) override; + bool moveCursor(const QPoint &position) override; DrmLease *lease() const; bool addLeaseObjects(QVector &objectList); @@ -75,10 +74,17 @@ private: bool m_setCursorSuccessful = false; bool m_moveCursorSuccessful = false; - bool m_cursorTextureDirty = true; - std::unique_ptr m_cursorTexture; QTimer m_turnOffTimer; DrmLease *m_lease = nullptr; + + struct { + QImage image; + QPoint hotspot; + QPoint position; + + std::unique_ptr texture; + qint64 cacheKey = 0; + } m_cursor; }; } diff --git a/src/backends/drm/drm_pipeline.cpp b/src/backends/drm/drm_pipeline.cpp index 4ba9d0ff24..315370557c 100644 --- a/src/backends/drm/drm_pipeline.cpp +++ b/src/backends/drm/drm_pipeline.cpp @@ -12,7 +12,6 @@ #include #include "core/session.h" -#include "cursor.h" #include "drm_backend.h" #include "drm_buffer.h" #include "drm_buffer_gbm.h" diff --git a/src/backends/wayland/wayland_output.cpp b/src/backends/wayland/wayland_output.cpp index 30279a27ac..00ba77916f 100644 --- a/src/backends/wayland/wayland_output.cpp +++ b/src/backends/wayland/wayland_output.cpp @@ -8,7 +8,6 @@ */ #include "wayland_output.h" #include "core/renderloop_p.h" -#include "cursor.h" #include "wayland_backend.h" #include "wayland_display.h" #include "wayland_server.h" @@ -60,7 +59,7 @@ void WaylandCursor::enable() { if (!m_enabled) { m_enabled = true; - update(); + sync(); } } @@ -68,23 +67,29 @@ void WaylandCursor::disable() { if (m_enabled) { m_enabled = false; - update(); + sync(); } } -void WaylandCursor::update() +void WaylandCursor::update(KWayland::Client::Buffer::Ptr buffer, qreal scale, const QPoint &hotspot) { - const QImage image = Cursors::self()->currentCursor()->image(); - if (!m_enabled || Cursors::self()->isCursorHidden() || image.isNull()) { + m_buffer = buffer; + m_scale = scale; + m_hotspot = hotspot; + + sync(); +} + +void WaylandCursor::sync() +{ + if (!m_enabled) { m_surface->attachBuffer(KWayland::Client::Buffer::Ptr()); m_surface->commit(KWayland::Client::Surface::CommitFlag::None); - m_hotspot = QPoint(); } else { - m_surface->attachBuffer(m_backend->display()->shmPool()->createBuffer(image)); - m_surface->setScale(std::ceil(image.devicePixelRatio())); - m_surface->damageBuffer(image.rect()); + m_surface->attachBuffer(m_buffer); + m_surface->setScale(std::ceil(m_scale)); + m_surface->damageBuffer(QRect(0, 0, INT32_MAX, INT32_MAX)); m_surface->commit(KWayland::Client::Surface::CommitFlag::None); - m_hotspot = Cursors::self()->currentCursor()->hotspot(); } if (m_pointer) { @@ -128,10 +133,6 @@ WaylandOutput::WaylandOutput(const QString &name, WaylandBackend *backend) connect(m_xdgShellSurface.get(), &XdgShellSurface::closeRequested, qApp, &QCoreApplication::quit); connect(this, &WaylandOutput::enabledChanged, this, &WaylandOutput::updateWindowTitle); connect(this, &WaylandOutput::dpmsModeChanged, this, &WaylandOutput::updateWindowTitle); - - connect(Cursors::self(), &Cursors::currentCursorChanged, this, [this]() { - m_cursor->update(); - }); } WaylandOutput::~WaylandOutput() @@ -161,9 +162,20 @@ RenderLoop *WaylandOutput::renderLoop() const return m_renderLoop.get(); } -bool WaylandOutput::usesSoftwareCursor() const +bool WaylandOutput::setCursor(const QImage &image, const QPoint &hotspot) { - return m_hasPointerLock; + KWayland::Client::Buffer::Ptr buffer; + if (!image.isNull()) { + buffer = m_backend->display()->shmPool()->createBuffer(image); + } + m_cursor->update(buffer, image.devicePixelRatio(), hotspot); + return !m_hasPointerLock; +} + +bool WaylandOutput::moveCursor(const QPoint &position) +{ + // The cursor position is controlled by the host compositor. + return !m_hasPointerLock; } void WaylandOutput::init(const QSize &pixelSize, qreal scale) diff --git a/src/backends/wayland/wayland_output.h b/src/backends/wayland/wayland_output.h index 107f7bf53f..12051b95e4 100644 --- a/src/backends/wayland/wayland_output.h +++ b/src/backends/wayland/wayland_output.h @@ -10,6 +10,7 @@ #include "core/output.h" +#include #include #include @@ -43,13 +44,17 @@ public: void enable(); void disable(); - void update(); + void update(KWayland::Client::Buffer::Ptr buffer, qreal scale, const QPoint &hotspot); private: + void sync(); + WaylandBackend *const m_backend; KWayland::Client::Pointer *m_pointer = nullptr; std::unique_ptr m_surface; + KWayland::Client::Buffer::Ptr m_buffer; QPoint m_hotspot; + qreal m_scale = 1; bool m_enabled = true; }; @@ -61,7 +66,8 @@ public: ~WaylandOutput() override; RenderLoop *renderLoop() const override; - bool usesSoftwareCursor() const override; + bool setCursor(const QImage &image, const QPoint &hotspot) override; + bool moveCursor(const QPoint &position) override; void init(const QSize &pixelSize, qreal scale); diff --git a/src/backends/x11/standalone/x11_standalone_output.cpp b/src/backends/x11/standalone/x11_standalone_output.cpp index 3f9ef9ecba..eca9d30562 100644 --- a/src/backends/x11/standalone/x11_standalone_output.cpp +++ b/src/backends/x11/standalone/x11_standalone_output.cpp @@ -58,11 +58,6 @@ void X11Output::setGammaRampSize(int size) m_gammaRampSize = size; } -bool X11Output::usesSoftwareCursor() const -{ - return false; -} - void X11Output::updateEnabled(bool enabled) { State next = m_state; diff --git a/src/backends/x11/standalone/x11_standalone_output.h b/src/backends/x11/standalone/x11_standalone_output.h index 8eaac7e3b3..9dc25a21f9 100644 --- a/src/backends/x11/standalone/x11_standalone_output.h +++ b/src/backends/x11/standalone/x11_standalone_output.h @@ -34,8 +34,6 @@ public: void updateEnabled(bool enabled); - bool usesSoftwareCursor() const override; - RenderLoop *renderLoop() const override; void setRenderLoop(RenderLoop *loop); diff --git a/src/backends/x11/windowed/x11_windowed_output.cpp b/src/backends/x11/windowed/x11_windowed_output.cpp index 1bf3a34536..5469d23458 100644 --- a/src/backends/x11/windowed/x11_windowed_output.cpp +++ b/src/backends/x11/windowed/x11_windowed_output.cpp @@ -12,7 +12,6 @@ #include #include "core/renderloop_p.h" -#include "cursor.h" #include "softwarevsyncmonitor.h" #include "x11_windowed_backend.h" @@ -198,10 +197,6 @@ void X11WindowedOutput::init(const QSize &pixelSize, qreal scale) addIcon(QSize(48, 48)); m_cursor = std::make_unique(this); - connect(Cursors::self(), &Cursors::currentCursorChanged, this, [this]() { - KWin::Cursor *c = KWin::Cursors::self()->currentCursor(); - m_cursor->update(c->image(), c->hotspot()); - }); xcb_map_window(m_backend->connection(), m_window); } @@ -263,9 +258,16 @@ void X11WindowedOutput::vblank(std::chrono::nanoseconds timestamp) renderLoopPrivate->notifyFrameCompleted(timestamp); } -bool X11WindowedOutput::usesSoftwareCursor() const +bool X11WindowedOutput::setCursor(const QImage &image, const QPoint &hotspot) { - return false; + m_cursor->update(image, hotspot); + return true; +} + +bool X11WindowedOutput::moveCursor(const QPoint &position) +{ + // The cursor position is controlled by the host compositor. + return true; } void X11WindowedOutput::updateEnabled(bool enabled) diff --git a/src/backends/x11/windowed/x11_windowed_output.h b/src/backends/x11/windowed/x11_windowed_output.h index 72d7c085fb..43fff8b556 100644 --- a/src/backends/x11/windowed/x11_windowed_output.h +++ b/src/backends/x11/windowed/x11_windowed_output.h @@ -78,7 +78,8 @@ public: */ QPointF mapFromGlobal(const QPointF &pos) const; - bool usesSoftwareCursor() const override; + bool setCursor(const QImage &image, const QPoint &hotspot) override; + bool moveCursor(const QPoint &position) override; QRegion exposedArea() const; void addExposedArea(const QRect &rect); diff --git a/src/composite.cpp b/src/composite.cpp index f4c04f61e8..42ec9fbcec 100644 --- a/src/composite.cpp +++ b/src/composite.cpp @@ -430,15 +430,30 @@ void Compositor::addOutput(Output *output) auto updateCursorLayer = [output, cursorLayer]() { const Cursor *cursor = Cursors::self()->currentCursor(); - cursorLayer->setVisible(cursor->isOnOutput(output) && output->usesSoftwareCursor()); - cursorLayer->setGeometry(output->mapFromGlobal(cursor->geometry())); + const QRect layerRect = output->mapFromGlobal(cursor->geometry()); + bool usesHardwareCursor = false; + if (!Cursors::self()->isCursorHidden()) { + usesHardwareCursor = output->setCursor(cursor->image(), cursor->hotspot()) && output->moveCursor(layerRect.topLeft()); + } else { + usesHardwareCursor = output->setCursor(QImage(), QPoint()); + } + cursorLayer->setVisible(cursor->isOnOutput(output) && !usesHardwareCursor); + cursorLayer->setGeometry(layerRect); + cursorLayer->addRepaintFull(); + }; + auto moveCursorLayer = [output, cursorLayer]() { + const Cursor *cursor = Cursors::self()->currentCursor(); + const QRect layerRect = output->mapFromGlobal(cursor->geometry()); + const bool usesHardwareCursor = output->moveCursor(layerRect.topLeft()); + cursorLayer->setVisible(cursor->isOnOutput(output) && !usesHardwareCursor); + cursorLayer->setGeometry(layerRect); cursorLayer->addRepaintFull(); }; updateCursorLayer(); connect(output, &Output::geometryChanged, cursorLayer, updateCursorLayer); connect(Cursors::self(), &Cursors::currentCursorChanged, cursorLayer, updateCursorLayer); connect(Cursors::self(), &Cursors::hiddenChanged, cursorLayer, updateCursorLayer); - connect(Cursors::self(), &Cursors::positionChanged, cursorLayer, updateCursorLayer); + connect(Cursors::self(), &Cursors::positionChanged, cursorLayer, moveCursorLayer); addSuperLayer(workspaceLayer); } diff --git a/src/core/output.cpp b/src/core/output.cpp index 63029a3fe5..37958429de 100644 --- a/src/core/output.cpp +++ b/src/core/output.cpp @@ -144,11 +144,6 @@ std::chrono::milliseconds Output::dimAnimationTime() return std::chrono::milliseconds(KSharedConfig::openConfig()->group("Effect-Kscreen").readEntry("Duration", 250)); } -bool Output::usesSoftwareCursor() const -{ - return true; -} - QRect Output::mapFromGlobal(const QRect &rect) const { return rect.translated(-geometry().topLeft()); @@ -421,4 +416,14 @@ Output::Transform Output::panelOrientation() const return m_information.panelOrientation; } +bool Output::setCursor(const QImage &image, const QPoint &hotspot) +{ + return false; +} + +bool Output::moveCursor(const QPoint &position) +{ + return false; +} + } // namespace KWin diff --git a/src/core/output.h b/src/core/output.h index cc45723eb2..9eae8b27f6 100644 --- a/src/core/output.h +++ b/src/core/output.h @@ -229,8 +229,6 @@ public: Q_ENUM(Transform) Transform transform() const; - virtual bool usesSoftwareCursor() const; - void applyChanges(const OutputConfiguration &config); SubPixel subPixel() const; @@ -262,6 +260,9 @@ public: virtual void setColorTransformation(const std::shared_ptr &transformation); + virtual bool setCursor(const QImage &image, const QPoint &hotspot); + virtual bool moveCursor(const QPoint &position); + Q_SIGNALS: /** * This signal is emitted when the geometry of this output has changed.