From 6a99bfd2f42f48e9e3b04359ec4fb43bf8bf8d3a Mon Sep 17 00:00:00 2001 From: Xaver Hugl Date: Sat, 25 Dec 2021 18:12:12 +0100 Subject: [PATCH] make software cursors work per output --- autotests/integration/pointer_input.cpp | 38 ++++---- autotests/integration/touch_input_test.cpp | 10 +-- src/abstract_output.cpp | 5 ++ src/abstract_output.h | 2 + src/backends/drm/drm_abstract_output.cpp | 20 ----- src/backends/drm/drm_abstract_output.h | 5 -- src/backends/drm/drm_backend.cpp | 86 ------------------- src/backends/drm/drm_backend.h | 6 -- src/backends/drm/drm_output.cpp | 70 ++++++++------- src/backends/drm/drm_output.h | 11 +-- src/backends/drm/egl_gbm_backend.cpp | 2 +- src/backends/fbdev/fb_backend.cpp | 2 - src/backends/virtual/virtual_backend.cpp | 2 - src/backends/wayland/wayland_backend.cpp | 3 +- src/backends/x11/standalone/x11_output.cpp | 5 ++ src/backends/x11/standalone/x11_output.h | 1 + src/backends/x11/standalone/x11_platform.cpp | 15 ++-- src/backends/x11/standalone/x11_platform.h | 5 +- .../x11/windowed/x11windowed_output.cpp | 5 ++ .../x11/windowed/x11windowed_output.h | 2 + src/composite.cpp | 3 - src/cursor.cpp | 26 +++++- src/cursor.h | 11 ++- src/effects.cpp | 4 +- src/hide_cursor_spy.cpp | 5 +- src/platform.cpp | 80 +---------------- src/platform.h | 77 ----------------- src/pointer_input.cpp | 10 +-- src/scene.cpp | 17 ++++ src/scene.h | 5 +- src/scenes/opengl/scene_opengl.cpp | 10 +-- src/scenes/opengl/scene_opengl.h | 2 +- src/scenes/qpainter/scene_qpainter.cpp | 6 +- src/scenes/qpainter/scene_qpainter.h | 2 +- 34 files changed, 165 insertions(+), 388 deletions(-) diff --git a/autotests/integration/pointer_input.cpp b/autotests/integration/pointer_input.cpp index a9ef7ad633..6b03583578 100644 --- a/autotests/integration/pointer_input.cpp +++ b/autotests/integration/pointer_input.cpp @@ -1736,27 +1736,27 @@ void PointerInputTest::testMoveCursor() void PointerInputTest::testHideShowCursor() { - QCOMPARE(kwinApp()->platform()->isCursorHidden(), false); - kwinApp()->platform()->hideCursor(); - QCOMPARE(kwinApp()->platform()->isCursorHidden(), true); - kwinApp()->platform()->showCursor(); - QCOMPARE(kwinApp()->platform()->isCursorHidden(), false); + QCOMPARE(Cursors::self()->isCursorHidden(), false); + Cursors::self()->hideCursor(); + QCOMPARE(Cursors::self()->isCursorHidden(), true); + Cursors::self()->showCursor(); + QCOMPARE(Cursors::self()->isCursorHidden(), false); - kwinApp()->platform()->hideCursor(); - QCOMPARE(kwinApp()->platform()->isCursorHidden(), true); - kwinApp()->platform()->hideCursor(); - kwinApp()->platform()->hideCursor(); - kwinApp()->platform()->hideCursor(); - QCOMPARE(kwinApp()->platform()->isCursorHidden(), true); + Cursors::self()->hideCursor(); + QCOMPARE(Cursors::self()->isCursorHidden(), true); + Cursors::self()->hideCursor(); + Cursors::self()->hideCursor(); + Cursors::self()->hideCursor(); + QCOMPARE(Cursors::self()->isCursorHidden(), true); - kwinApp()->platform()->showCursor(); - QCOMPARE(kwinApp()->platform()->isCursorHidden(), true); - kwinApp()->platform()->showCursor(); - QCOMPARE(kwinApp()->platform()->isCursorHidden(), true); - kwinApp()->platform()->showCursor(); - QCOMPARE(kwinApp()->platform()->isCursorHidden(), true); - kwinApp()->platform()->showCursor(); - QCOMPARE(kwinApp()->platform()->isCursorHidden(), false); + Cursors::self()->showCursor(); + QCOMPARE(Cursors::self()->isCursorHidden(), true); + Cursors::self()->showCursor(); + QCOMPARE(Cursors::self()->isCursorHidden(), true); + Cursors::self()->showCursor(); + QCOMPARE(Cursors::self()->isCursorHidden(), true); + Cursors::self()->showCursor(); + QCOMPARE(Cursors::self()->isCursorHidden(), false); } void PointerInputTest::testDefaultInputRegion() diff --git a/autotests/integration/touch_input_test.cpp b/autotests/integration/touch_input_test.cpp index 8c8b70ace2..d5904ddaca 100644 --- a/autotests/integration/touch_input_test.cpp +++ b/autotests/integration/touch_input_test.cpp @@ -122,26 +122,26 @@ AbstractClient *TouchInputTest::showWindow(bool decorated) void TouchInputTest::testTouchHidesCursor() { - QCOMPARE(kwinApp()->platform()->isCursorHidden(), false); + QCOMPARE(Cursors::self()->isCursorHidden(), false); quint32 timestamp = 1; kwinApp()->platform()->touchDown(1, QPointF(125, 125), timestamp++); - QCOMPARE(kwinApp()->platform()->isCursorHidden(), true); + QCOMPARE(Cursors::self()->isCursorHidden(), true); kwinApp()->platform()->touchDown(2, QPointF(130, 125), timestamp++); kwinApp()->platform()->touchUp(2, timestamp++); kwinApp()->platform()->touchUp(1, timestamp++); // now a mouse event should show the cursor again kwinApp()->platform()->pointerMotion(QPointF(0, 0), timestamp++); - QCOMPARE(kwinApp()->platform()->isCursorHidden(), false); + QCOMPARE(Cursors::self()->isCursorHidden(), false); // touch should hide again kwinApp()->platform()->touchDown(1, QPointF(125, 125), timestamp++); kwinApp()->platform()->touchUp(1, timestamp++); - QCOMPARE(kwinApp()->platform()->isCursorHidden(), true); + QCOMPARE(Cursors::self()->isCursorHidden(), true); // wheel should also show kwinApp()->platform()->pointerAxisVertical(1.0, timestamp++); - QCOMPARE(kwinApp()->platform()->isCursorHidden(), false); + QCOMPARE(Cursors::self()->isCursorHidden(), false); } void TouchInputTest::testMultipleTouchPoints_data() diff --git a/src/abstract_output.cpp b/src/abstract_output.cpp index 4c6f33a17e..832838f8c6 100644 --- a/src/abstract_output.cpp +++ b/src/abstract_output.cpp @@ -166,4 +166,9 @@ std::chrono::milliseconds AbstractOutput::dimAnimationTime() return std::chrono::milliseconds (KSharedConfig::openConfig()->group("Effect-Kscreen").readEntry("Duration", 250)); } +bool AbstractOutput::usesSoftwareCursor() const +{ + return true; +} + } // namespace KWin diff --git a/src/abstract_output.h b/src/abstract_output.h index ed2fca80ac..dd74a175d6 100644 --- a/src/abstract_output.h +++ b/src/abstract_output.h @@ -213,6 +213,8 @@ public: Q_ENUM(Transform) virtual Transform transform() const { return Transform::Normal; } + virtual bool usesSoftwareCursor() const; + Q_SIGNALS: /** * This signal is emitted when the geometry of this output has changed. diff --git a/src/backends/drm/drm_abstract_output.cpp b/src/backends/drm/drm_abstract_output.cpp index f86e080c47..04f5685d74 100644 --- a/src/backends/drm/drm_abstract_output.cpp +++ b/src/backends/drm/drm_abstract_output.cpp @@ -20,26 +20,6 @@ DrmAbstractOutput::DrmAbstractOutput(DrmGpu *gpu) { } -bool DrmAbstractOutput::showCursor() -{ - return true; -} - -bool DrmAbstractOutput::hideCursor() -{ - return true; -} - -bool DrmAbstractOutput::updateCursor() -{ - return true; -} - -bool DrmAbstractOutput::moveCursor() -{ - return true; -} - DrmGpu *DrmAbstractOutput::gpu() const { return m_gpu; diff --git a/src/backends/drm/drm_abstract_output.h b/src/backends/drm/drm_abstract_output.h index 595a29556d..11f6f1e524 100644 --- a/src/backends/drm/drm_abstract_output.h +++ b/src/backends/drm/drm_abstract_output.h @@ -22,11 +22,6 @@ class DrmAbstractOutput : public AbstractWaylandOutput { Q_OBJECT public: - virtual bool showCursor(); - virtual bool hideCursor(); - virtual bool updateCursor(); - virtual bool moveCursor(); - virtual bool present(const QSharedPointer &buffer, QRegion damagedRegion) = 0; virtual bool needsSoftwareTransformation() const = 0; diff --git a/src/backends/drm/drm_backend.cpp b/src/backends/drm/drm_backend.cpp index 62a59938fb..d6260c51e2 100644 --- a/src/backends/drm/drm_backend.cpp +++ b/src/backends/drm/drm_backend.cpp @@ -158,7 +158,6 @@ void DrmBackend::reactivate() // While the session had been inactive, an output could have been added or // removed, we need to re-scan outputs. updateOutputs(); - updateCursor(); Q_EMIT activeChanged(); } @@ -210,8 +209,6 @@ bool DrmBackend::initialize() return false; } - initCursor(); - // setup udevMonitor if (m_udevMonitor) { m_udevMonitor->filterSubsystemDevType("drm"); @@ -240,7 +237,6 @@ void DrmBackend::handleUdevEvent() qCDebug(KWIN_DRM) << "New gpu found:" << device->devNode(); if (addGpu(device->devNode())) { updateOutputs(); - updateCursor(); } } else if (device->action() == QStringLiteral("remove")) { DrmGpu *gpu = findGpu(device->devNum()); @@ -255,7 +251,6 @@ void DrmBackend::handleUdevEvent() m_gpus.removeOne(gpu); delete gpu; updateOutputs(); - updateCursor(); } } } else if (device->action() == QStringLiteral("change")) { @@ -266,7 +261,6 @@ void DrmBackend::handleUdevEvent() if (gpu) { qCDebug(KWIN_DRM) << "Received change event for monitored drm device" << gpu->devNode(); updateOutputs(); - updateCursor(); } } } @@ -517,85 +511,6 @@ void DrmBackend::enableOutput(DrmAbstractOutput *output, bool enable) } } -void DrmBackend::initCursor() -{ - connect(Cursors::self(), &Cursors::currentCursorChanged, this, &DrmBackend::updateCursor); - connect(Cursors::self(), &Cursors::positionChanged, this, &DrmBackend::moveCursor); -} - -void DrmBackend::updateCursor() -{ - if (isSoftwareCursorForced() || isCursorHidden()) { - return; - } - - auto cursor = Cursors::self()->currentCursor(); - if (cursor->image().isNull()) { - doHideCursor(); - return; - } - - bool success = true; - - for (DrmAbstractOutput *output : qAsConst(m_outputs)) { - success = output->updateCursor(); - if (!success) { - qCDebug(KWIN_DRM) << "Failed to update cursor on output" << output->name(); - break; - } - success = output->showCursor(); - if (!success) { - qCDebug(KWIN_DRM) << "Failed to show cursor on output" << output->name(); - break; - } - success = output->moveCursor(); - if (!success) { - qCDebug(KWIN_DRM) << "Failed to move cursor on output" << output->name(); - break; - } - } - - setSoftwareCursor(!success); -} - -void DrmBackend::doShowCursor() -{ - if (usesSoftwareCursor()) { - return; - } - updateCursor(); -} - -void DrmBackend::doHideCursor() -{ - if (usesSoftwareCursor()) { - return; - } - for (auto it = m_outputs.constBegin(); it != m_outputs.constEnd(); ++it) { - (*it)->hideCursor(); - } -} - -void DrmBackend::doSetSoftwareCursor() -{ - if (isCursorHidden() || !usesSoftwareCursor()) { - return; - } - for (auto it = m_outputs.constBegin(); it != m_outputs.constEnd(); ++it) { - (*it)->hideCursor(); - } -} - -void DrmBackend::moveCursor() -{ - if (isCursorHidden() || usesSoftwareCursor()) { - return; - } - for (auto it = m_outputs.constBegin(); it != m_outputs.constEnd(); ++it) { - (*it)->moveCursor(); - } -} - InputBackend *DrmBackend::createInputBackend() { return new LibinputBackend(); @@ -723,7 +638,6 @@ bool DrmBackend::applyOutputChanges(const WaylandOutputConfig &config) output->applyChanges(config); } }; - updateCursor(); if (Compositor::compositing()) { Compositor::self()->scene()->addRepaintFull(); } diff --git a/src/backends/drm/drm_backend.h b/src/backends/drm/drm_backend.h index 5cc084ae3c..368409319f 100644 --- a/src/backends/drm/drm_backend.h +++ b/src/backends/drm/drm_backend.h @@ -76,9 +76,6 @@ Q_SIGNALS: void gpuAdded(DrmGpu *gpu); protected: - void doHideCursor() override; - void doShowCursor() override; - void doSetSoftwareCursor() override; bool applyOutputChanges(const WaylandOutputConfig &config) override; private: @@ -89,9 +86,6 @@ private: void reactivate(); void deactivate(); void updateOutputs(); - void updateCursor(); - void moveCursor(); - void initCursor(); void readOutputsConfiguration(const QVector &outputs); void handleUdevEvent(); DrmGpu *addGpu(const QString &fileName); diff --git a/src/backends/drm/drm_output.cpp b/src/backends/drm/drm_output.cpp index b65271d802..cdc431b356 100644 --- a/src/backends/drm/drm_output.cpp +++ b/src/backends/drm/drm_output.cpp @@ -25,6 +25,7 @@ #include "session.h" #include "waylandoutputconfig.h" #include "dumb_swapchain.h" +#include "cursor.h" // Qt #include #include @@ -69,6 +70,9 @@ DrmOutput::DrmOutput(DrmPipeline *pipeline) connect(&m_turnOffTimer, &QTimer::timeout, this, [this] { setDrmDpmsMode(DpmsMode::Off); }); + + connect(Cursors::self(), &Cursors::currentCursorChanged, this, &DrmOutput::updateCursor); + connect(Cursors::self(), &Cursors::positionChanged, this, &DrmOutput::moveCursor); } DrmOutput::~DrmOutput() @@ -76,23 +80,6 @@ DrmOutput::~DrmOutput() m_pipeline->setOutput(nullptr); } -bool DrmOutput::hideCursor() -{ - if (!isEnabled() || !m_connector->isConnected()) { - return true; - } - return m_pipeline->setCursor(nullptr); -} - -bool DrmOutput::showCursor() -{ - if (!isEnabled() || !m_connector->isConnected()) { - return true; - } - const Cursor * const cursor = Cursors::self()->currentCursor(); - return m_pipeline->setCursor(m_cursor->currentBuffer(), logicalToNativeMatrix(cursor->rect(), scale(), transform()).map(cursor->hotspot())); -} - static bool isCursorSpriteCompatible(const QImage *buffer, const QImage *sprite) { // Note that we need compare the rects in the device independent pixels because the @@ -103,23 +90,24 @@ static bool isCursorSpriteCompatible(const QImage *buffer, const QImage *sprite) return bufferRect.contains(spriteRect); } -bool DrmOutput::updateCursor() +void DrmOutput::updateCursor() { if (!isEnabled() || !m_connector->isConnected()) { - return true; + return; } const Cursor *cursor = Cursors::self()->currentCursor(); if (!cursor) { - hideCursor(); - return true; + m_pipeline->setCursor(nullptr); + return; } const QImage cursorImage = cursor->image(); if (cursorImage.isNull()) { - hideCursor(); - return true; + m_pipeline->setCursor(nullptr); + return; } if (m_cursor && m_cursor->isEmpty()) { - return false; + m_pipeline->setCursor(nullptr); + return; } const auto plane = m_pipeline->pending.crtc->cursorPlane(); if (!m_cursor || (plane && !plane->formats()[m_cursor->drmFormat()].contains(DRM_FORMAT_MOD_LINEAR))) { @@ -135,14 +123,13 @@ bool DrmOutput::updateCursor() break; } } - if (!m_cursor || m_cursor->isEmpty()) { - return false; - } } else { m_cursor = QSharedPointer::create(m_gpu, m_gpu->cursorSize(), DRM_FORMAT_XRGB8888, QImage::Format::Format_ARGB32_Premultiplied); - if (m_cursor->isEmpty()) { - return false; - } + } + if (!m_cursor || m_cursor->isEmpty()) { + m_pipeline->setCursor(nullptr); + m_setCursorSuccessful = false; + return; } } m_cursor->releaseBuffer(m_cursor->currentBuffer()); @@ -151,7 +138,9 @@ bool DrmOutput::updateCursor() c->setDevicePixelRatio(scale()); if (!isCursorSpriteCompatible(c, &cursorImage)) { // If the cursor image is too big, fall back to rendering the software cursor. - return false; + m_pipeline->setCursor(nullptr); + m_setCursorSuccessful = false; + return; } c->fill(Qt::transparent); QPainter p; @@ -160,18 +149,22 @@ bool DrmOutput::updateCursor() p.setRenderHint(QPainter::SmoothPixmapTransform); p.drawImage(QPoint(0, 0), cursorImage); p.end(); - return m_pipeline->setCursor(m_cursor->currentBuffer(), logicalToNativeMatrix(cursor->rect(), scale(), transform()).map(cursor->hotspot())); + m_setCursorSuccessful = m_pipeline->setCursor(m_cursor->currentBuffer(), logicalToNativeMatrix(cursor->rect(), scale(), transform()).map(cursor->hotspot())); + moveCursor(); } -bool DrmOutput::moveCursor() +void DrmOutput::moveCursor() { - if (!isEnabled() || !m_connector->isConnected()) { - return true; + if (!m_setCursorSuccessful) { + return; } Cursor *cursor = Cursors::self()->currentCursor(); const QMatrix4x4 monitorMatrix = logicalToNativeMatrix(geometry(), scale(), transform()); const QMatrix4x4 hotspotMatrix = logicalToNativeMatrix(cursor->rect(), scale(), transform()); - return m_pipeline->moveCursor(monitorMatrix.map(cursor->pos()) - hotspotMatrix.map(cursor->hotspot())); + m_moveCursorSuccessful = m_pipeline->moveCursor(monitorMatrix.map(cursor->pos()) - hotspotMatrix.map(cursor->hotspot())); + if (!m_moveCursorSuccessful) { + m_pipeline->setCursor(nullptr); + } } QVector DrmOutput::getModes() const @@ -491,4 +484,9 @@ int DrmOutput::maxBpc() const return prop ? prop->maxValue() : 8; } +bool DrmOutput::usesSoftwareCursor() const +{ + return !m_setCursorSuccessful || !m_moveCursorSuccessful; +} + } diff --git a/src/backends/drm/drm_output.h b/src/backends/drm/drm_output.h index 949eaa905a..a32edee211 100644 --- a/src/backends/drm/drm_output.h +++ b/src/backends/drm/drm_output.h @@ -43,11 +43,6 @@ public: DrmOutput(DrmPipeline *pipeline); ~DrmOutput() override; - bool showCursor() override; - bool hideCursor() override; - bool updateCursor() override; - bool moveCursor() override; - bool present(const QSharedPointer &buffer, QRegion damagedRegion) override; DrmConnector *connector() const; @@ -66,6 +61,7 @@ public: void pageFlipped(std::chrono::nanoseconds timestamp); void presentFailed(); + bool usesSoftwareCursor() const override; private: void initOutputDevice(); @@ -80,11 +76,16 @@ private: int gammaRampSize() const override; bool setGammaRamp(const GammaRamp &gamma) override; + void updateCursor(); + void moveCursor(); DrmPipeline *m_pipeline; DrmConnector *m_connector; QSharedPointer m_cursor; + bool m_setCursorSuccessful = false; + bool m_moveCursorSuccessful = false; + QRect m_lastCursorGeometry; QTimer m_turnOffTimer; }; diff --git a/src/backends/drm/egl_gbm_backend.cpp b/src/backends/drm/egl_gbm_backend.cpp index b4a182bc6c..811b8863e6 100644 --- a/src/backends/drm/egl_gbm_backend.cpp +++ b/src/backends/drm/egl_gbm_backend.cpp @@ -773,7 +773,7 @@ QSharedPointer EglGbmBackend::textureForOutput(AbstractOutput *output bool EglGbmBackend::directScanoutAllowed(AbstractOutput *output) const { - return !m_backend->usesSoftwareCursor() && !output->directScanoutInhibited(); + return !output->usesSoftwareCursor() && !output->directScanoutInhibited(); } bool EglGbmBackend::hasOutput(AbstractOutput *output) const diff --git a/src/backends/fbdev/fb_backend.cpp b/src/backends/fbdev/fb_backend.cpp index 6a9f488288..e0459e0337 100644 --- a/src/backends/fbdev/fb_backend.cpp +++ b/src/backends/fbdev/fb_backend.cpp @@ -108,8 +108,6 @@ Session *FramebufferBackend::session() const bool FramebufferBackend::initialize() { - setSoftwareCursorForced(true); - QString framebufferDevice = deviceIdentifier(); if (framebufferDevice.isEmpty()) { const auto fbs = Udev().listFramebuffers(); diff --git a/src/backends/virtual/virtual_backend.cpp b/src/backends/virtual/virtual_backend.cpp index 2a78364624..f3a19b2bde 100644 --- a/src/backends/virtual/virtual_backend.cpp +++ b/src/backends/virtual/virtual_backend.cpp @@ -195,8 +195,6 @@ bool VirtualBackend::initialize() Q_EMIT outputAdded(dummyOutput); Q_EMIT outputEnabled(dummyOutput); } - - setSoftwareCursorForced(true); setReady(true); Q_EMIT screensQueried(); diff --git a/src/backends/wayland/wayland_backend.cpp b/src/backends/wayland/wayland_backend.cpp index 339afb3997..96b479060c 100644 --- a/src/backends/wayland/wayland_backend.cpp +++ b/src/backends/wayland/wayland_backend.cpp @@ -703,8 +703,6 @@ bool WaylandBackend::initialize() return; } m_waylandCursor->installImage(); - auto c = Cursors::self()->currentCursor(); - Q_EMIT c->rendered(c->geometry()); } ); connect(Cursors::self(), &Cursors::positionChanged, this, @@ -1051,6 +1049,7 @@ void WaylandBackend::removeVirtualOutput(AbstractOutput *output) delete waylandOutput; } } + } } // KWin diff --git a/src/backends/x11/standalone/x11_output.cpp b/src/backends/x11/standalone/x11_output.cpp index 874105d372..6032762258 100644 --- a/src/backends/x11/standalone/x11_output.cpp +++ b/src/backends/x11/standalone/x11_output.cpp @@ -98,4 +98,9 @@ QSize X11Output::pixelSize() const return geometry().size(); } +bool X11Output::usesSoftwareCursor() const +{ + return false; +} + } diff --git a/src/backends/x11/standalone/x11_output.h b/src/backends/x11/standalone/x11_output.h index 1f2f82b69f..b3df14723b 100644 --- a/src/backends/x11/standalone/x11_output.h +++ b/src/backends/x11/standalone/x11_output.h @@ -48,6 +48,7 @@ public: void setPhysicalSize(const QSize &size); QSize pixelSize() const override; + bool usesSoftwareCursor() const override; private: void setCrtc(xcb_randr_crtc_t crtc); diff --git a/src/backends/x11/standalone/x11_platform.cpp b/src/backends/x11/standalone/x11_platform.cpp index 474ba074ff..c47d73b4cd 100644 --- a/src/backends/x11/standalone/x11_platform.cpp +++ b/src/backends/x11/standalone/x11_platform.cpp @@ -142,6 +142,8 @@ bool X11StandalonePlatform::initialize() if (Xcb::Extensions::self()->isRandrAvailable()) { m_randrEventFilter.reset(new XrandrEventFilter(this)); } + connect(Cursors::self(), &Cursors::currentCursorChanged, this, &X11StandalonePlatform::updateCursor); + updateCursor(); return true; } @@ -333,14 +335,13 @@ PlatformCursorImage X11StandalonePlatform::cursorImage() const return PlatformCursorImage(qcursorimg.copy(), QPoint(cursor->xhot, cursor->yhot)); } -void X11StandalonePlatform::doHideCursor() +void X11StandalonePlatform::updateCursor() { - xcb_xfixes_hide_cursor(kwinApp()->x11Connection(), kwinApp()->x11RootWindow()); -} - -void X11StandalonePlatform::doShowCursor() -{ - xcb_xfixes_show_cursor(kwinApp()->x11Connection(), kwinApp()->x11RootWindow()); + if (Cursors::self()->isCursorHidden()) { + xcb_xfixes_hide_cursor(kwinApp()->x11Connection(), kwinApp()->x11RootWindow()); + } else { + xcb_xfixes_show_cursor(kwinApp()->x11Connection(), kwinApp()->x11RootWindow()); + } } void X11StandalonePlatform::startInteractiveWindowSelection(std::function callback, const QByteArray &cursorName) diff --git a/src/backends/x11/standalone/x11_platform.h b/src/backends/x11/standalone/x11_platform.h index 189a20d538..b1c22dada7 100644 --- a/src/backends/x11/standalone/x11_platform.h +++ b/src/backends/x11/standalone/x11_platform.h @@ -68,10 +68,6 @@ public: Outputs outputs() const override; Outputs enabledOutputs() const override; -protected: - void doHideCursor() override; - void doShowCursor() override; - private: /** * Tests whether GLX is supported and returns @c true @@ -88,6 +84,7 @@ private: template void doUpdateOutputs(); void updateRefreshRate(); + void updateCursor(); Session *m_session; XInputIntegration *m_xinputIntegration = nullptr; diff --git a/src/backends/x11/windowed/x11windowed_output.cpp b/src/backends/x11/windowed/x11windowed_output.cpp index a15cfe6e05..0b98b549f9 100644 --- a/src/backends/x11/windowed/x11windowed_output.cpp +++ b/src/backends/x11/windowed/x11windowed_output.cpp @@ -180,4 +180,9 @@ void X11WindowedOutput::vblank(std::chrono::nanoseconds timestamp) renderLoopPrivate->notifyFrameCompleted(timestamp); } +bool X11WindowedOutput::usesSoftwareCursor() const +{ + return false; +} + } diff --git a/src/backends/x11/windowed/x11windowed_output.h b/src/backends/x11/windowed/x11windowed_output.h index bfd24da964..523949376e 100644 --- a/src/backends/x11/windowed/x11windowed_output.h +++ b/src/backends/x11/windowed/x11windowed_output.h @@ -65,6 +65,8 @@ public: */ QPointF mapFromGlobal(const QPointF &pos) const; + bool usesSoftwareCursor() const override; + private: void initXInputForWindow(); void vblank(std::chrono::nanoseconds timestamp); diff --git a/src/composite.cpp b/src/composite.cpp index 196a3c92df..26bbce65e5 100644 --- a/src/composite.cpp +++ b/src/composite.cpp @@ -649,9 +649,6 @@ void Compositor::composite(RenderLoop *renderLoop) surface->frameRendered(frameTime.count()); } } - if (!kwinApp()->platform()->isCursorHidden()) { - Cursors::self()->currentCursor()->markAsRendered(); - } } } diff --git a/src/cursor.cpp b/src/cursor.cpp index e04d72277a..fa55babede 100644 --- a/src/cursor.cpp +++ b/src/cursor.cpp @@ -15,6 +15,9 @@ #include "platform.h" #include "utils.h" #include "xcbutils.h" +#include "abstract_output.h" +#include "composite.h" +#include "scene.h" // KDE #include #include @@ -60,6 +63,27 @@ void Cursors::removeCursor(Cursor* cursor) } } +void Cursors::hideCursor() +{ + m_cursorHideCounter++; + if (m_cursorHideCounter == 1) { + Q_EMIT currentCursorChanged(m_currentCursor); + } +} + +void Cursors::showCursor() +{ + m_cursorHideCounter--; + if (m_cursorHideCounter == 0) { + Q_EMIT currentCursorChanged(m_currentCursor); + } +} + +bool Cursors::isCursorHidden() const +{ + return m_cursorHideCounter > 0; +} + void Cursors::setCurrentCursor(Cursor* cursor) { if (m_currentCursor == cursor) @@ -68,11 +92,9 @@ void Cursors::setCurrentCursor(Cursor* cursor) Q_ASSERT(m_cursors.contains(cursor) || !cursor); if (m_currentCursor) { - disconnect(m_currentCursor, &Cursor::rendered, this, &Cursors::currentCursorRendered); disconnect(m_currentCursor, &Cursor::cursorChanged, this, &Cursors::emitCurrentCursorChanged); } m_currentCursor = cursor; - connect(m_currentCursor, &Cursor::rendered, this, &Cursors::currentCursorRendered); connect(m_currentCursor, &Cursor::cursorChanged, this, &Cursors::emitCurrentCursorChanged); Q_EMIT currentCursorChanged(m_currentCursor); diff --git a/src/cursor.h b/src/cursor.h index 744e5677fd..2d16bf0d38 100644 --- a/src/cursor.h +++ b/src/cursor.h @@ -166,9 +166,6 @@ public: QRect rect() const; void updateCursor(const QImage &image, const QPoint &hotspot); - void markAsRendered() { - Q_EMIT rendered(geometry()); - } Q_SIGNALS: void posChanged(const QPoint& pos); @@ -186,8 +183,6 @@ Q_SIGNALS: void cursorChanged(); void themeChanged(); - void rendered(const QRect &geometry); - protected: /** * Performs the actual warping of the cursor. @@ -275,11 +270,14 @@ public: return m_currentCursor; } + void hideCursor(); + void showCursor(); + bool isCursorHidden() const; + static Cursors* self(); Q_SIGNALS: void currentCursorChanged(Cursor* cursor); - void currentCursorRendered(const QRect &geometry); void positionChanged(Cursor* cursor, const QPoint &position); private: @@ -290,6 +288,7 @@ private: Cursor* m_currentCursor = nullptr; Cursor* m_mouse = nullptr; QVector m_cursors; + int m_cursorHideCounter = 0; }; class InputConfig diff --git a/src/effects.cpp b/src/effects.cpp index 0e56c5104b..e8cd375c45 100644 --- a/src/effects.cpp +++ b/src/effects.cpp @@ -1595,12 +1595,12 @@ PlatformCursorImage EffectsHandlerImpl::cursorImage() const void EffectsHandlerImpl::hideCursor() { - kwinApp()->platform()->hideCursor(); + Cursors::self()->hideCursor(); } void EffectsHandlerImpl::showCursor() { - kwinApp()->platform()->showCursor(); + Cursors::self()->showCursor(); } void EffectsHandlerImpl::startInteractiveWindowSelection(std::function callback) diff --git a/src/hide_cursor_spy.cpp b/src/hide_cursor_spy.cpp index c735352966..af6c19f413 100644 --- a/src/hide_cursor_spy.cpp +++ b/src/hide_cursor_spy.cpp @@ -10,6 +10,7 @@ #include "main.h" #include "platform.h" #include "input_event.h" +#include "cursor.h" namespace KWin { @@ -49,7 +50,7 @@ void HideCursorSpy::showCursor() return; } m_cursorHidden = false; - kwinApp()->platform()->showCursor(); + Cursors::self()->showCursor(); } void HideCursorSpy::hideCursor() @@ -58,7 +59,7 @@ void HideCursorSpy::hideCursor() return; } m_cursorHidden = true; - kwinApp()->platform()->hideCursor(); + Cursors::self()->hideCursor(); } } diff --git a/src/platform.cpp b/src/platform.cpp index 021a0d8e49..1722e1e8a2 100644 --- a/src/platform.cpp +++ b/src/platform.cpp @@ -39,9 +39,6 @@ Platform::Platform(QObject *parent) : QObject(parent) , m_eglDisplay(EGL_NO_DISPLAY) { - setSoftwareCursorForced(false); - connect(Cursors::self(), &Cursors::currentCursorRendered, this, &Platform::cursorRendered); - connect(this, &Platform::outputDisabled, this, [this] (AbstractOutput *output) { if (m_primaryOutput == output) { setPrimaryOutput(enabledOutputs().value(0, nullptr)); @@ -64,30 +61,6 @@ PlatformCursorImage Platform::cursorImage() const return PlatformCursorImage(cursor->image(), cursor->hotspot()); } -void Platform::hideCursor() -{ - m_hideCursorCounter++; - if (m_hideCursorCounter == 1) { - doHideCursor(); - } -} - -void Platform::doHideCursor() -{ -} - -void Platform::showCursor() -{ - m_hideCursorCounter--; - if (m_hideCursorCounter == 0) { - doShowCursor(); - } -} - -void Platform::doShowCursor() -{ -} - InputBackend *Platform::createInputBackend() { return nullptr; @@ -236,55 +209,6 @@ AbstractOutput *Platform::outputAt(const QPoint &pos) const return bestOutput; } -bool Platform::usesSoftwareCursor() const -{ - return m_softwareCursor; -} - -void Platform::setSoftwareCursor(bool set) -{ - if (m_softwareCursor == set) { - return; - } - m_softwareCursor = set; - doSetSoftwareCursor(); - if (m_softwareCursor) { - connect(Cursors::self(), &Cursors::positionChanged, this, &Platform::triggerCursorRepaint); - connect(Cursors::self(), &Cursors::currentCursorChanged, this, &Platform::triggerCursorRepaint); - } else { - disconnect(Cursors::self(), &Cursors::positionChanged, this, &Platform::triggerCursorRepaint); - disconnect(Cursors::self(), &Cursors::currentCursorChanged, this, &Platform::triggerCursorRepaint); - } - triggerCursorRepaint(); -} - -void Platform::doSetSoftwareCursor() -{ -} - -bool Platform::isSoftwareCursorForced() const -{ - return m_softwareCursorForced; -} - -void Platform::setSoftwareCursorForced(bool forced) -{ - if (qEnvironmentVariableIsSet("KWIN_FORCE_SW_CURSOR")) { - forced = true; - } - if (m_softwareCursorForced == forced) { - return; - } - m_softwareCursorForced = forced; - if (m_softwareCursorForced) { - setSoftwareCursor(true); - } else { - // Do not unset the software cursor yet, the platform will choose the right - // moment when it can be done. There is still a chance that we must continue - // using the software cursor. - } -} - void Platform::triggerCursorRepaint() { if (Compositor::compositing()) { @@ -295,9 +219,7 @@ void Platform::triggerCursorRepaint() void Platform::cursorRendered(const QRect &geometry) { - if (m_softwareCursor) { - m_cursor.lastRenderedGeometry = geometry; - } + m_cursor.lastRenderedGeometry = geometry; } void Platform::keyboardKeyPressed(quint32 key, quint32 time) diff --git a/src/platform.h b/src/platform.h index e180d173f6..14f70583ee 100644 --- a/src/platform.h +++ b/src/platform.h @@ -219,26 +219,6 @@ public: */ virtual void setupActionForGlobalAccel(QAction *action); - /** - * Returns @c true if the software cursor is being used; otherwise returns @c false. - */ - bool usesSoftwareCursor() const; - - /** - * Returns @c true if the software cursor is being forced; otherwise returns @c false. - * - * Note that the value returned by this function not always matches usesSoftwareCursor(). - * If this function returns @c true, then it is guaranteed that the compositor will - * use the software cursor. However, this doesn't apply vice versa. - * - * If the compositor uses a software cursor, this function may return @c false. This - * is typically the case if the current cursor image can't be displayed using hardware - * cursors, for example due to buffer size limitations, etc. - * - * @see usesSoftwareCursor() - */ - bool isSoftwareCursorForced() const; - /** * Returns a PlatformCursorImage. By default this is created by softwareCursor and * softwareCursorHotspot. An implementing subclass can use this to provide a better @@ -250,33 +230,6 @@ public: */ virtual PlatformCursorImage cursorImage() const; - /** - * The Platform cursor image should be hidden. - * @see showCursor - * @see doHideCursor - * @see isCursorHidden - * @since 5.9 - */ - void hideCursor(); - - /** - * The Platform cursor image should be shown again. - * @see hideCursor - * @see doShowCursor - * @see isCursorHidden - * @since 5.9 - */ - void showCursor(); - - /** - * Whether the cursor is currently hidden. - * @see showCursor - * @see hideCursor - * @since 5.9 - */ - bool isCursorHidden() const { - return m_hideCursorCounter > 0; - } bool isReady() const { return m_ready; } @@ -478,8 +431,6 @@ Q_SIGNALS: protected: explicit Platform(QObject *parent = nullptr); - void setSoftwareCursor(bool set); - void setSoftwareCursorForced(bool forced); void repaint(const QRect &rect); void setReady(bool ready); void setPerScreenRenderingEnabled(bool enabled); @@ -503,35 +454,8 @@ protected: m_supportsOutputChanges = true; } - /** - * Actual platform specific way to hide the cursor. - * Sub-classes need to implement if they support hiding the cursor. - * - * This method is invoked by hideCursor if the cursor needs to be hidden. - * The default implementation does nothing. - * - * @see doShowCursor - * @see hideCursor - * @see showCursor - */ - virtual void doHideCursor(); - /** - * Actual platform specific way to show the cursor. - * Sub-classes need to implement if they support showing the cursor. - * - * This method is invoked by showCursor if the cursor needs to be shown again. - * - * @see doShowCursor - * @see hideCursor - * @see showCursor - */ - virtual void doShowCursor(); - virtual void doSetSoftwareCursor(); - private: void triggerCursorRepaint(); - bool m_softwareCursor = false; - bool m_softwareCursorForced = false; struct { QRect lastRenderedGeometry; } m_cursor; @@ -543,7 +467,6 @@ private: qreal m_initialOutputScale = 1; EGLDisplay m_eglDisplay; EGLContext m_globalShareContext = EGL_NO_CONTEXT; - int m_hideCursorCounter = 0; bool m_supportsGammaControl = false; bool m_supportsOutputChanges = false; bool m_isPerScreenRenderingEnabled = false; diff --git a/src/pointer_input.cpp b/src/pointer_input.cpp index 6019246a7f..3993e24570 100644 --- a/src/pointer_input.cpp +++ b/src/pointer_input.cpp @@ -121,13 +121,13 @@ void PointerInputRedirection::init() InputDeviceHandler::init(); if (!input()->hasPointer()) { - kwinApp()->platform()->hideCursor(); + Cursors::self()->hideCursor(); } - connect(input(), &InputRedirection::hasPointerChanged, this, [this]() { + connect(input(), &InputRedirection::hasPointerChanged, this, []() { if (input()->hasPointer()) { - kwinApp()->platform()->showCursor(); + Cursors::self()->showCursor(); } else { - kwinApp()->platform()->hideCursor(); + Cursors::self()->hideCursor(); } }); @@ -138,8 +138,6 @@ void PointerInputRedirection::init() }); Q_EMIT m_cursor->changed(); - connect(Cursors::self()->mouse(), &Cursor::rendered, m_cursor, &CursorImage::markAsRendered); - connect(screens(), &Screens::changed, this, &PointerInputRedirection::updateAfterScreenChange); if (waylandServer()->hasScreenLockerIntegration()) { connect(ScreenLocker::KSldApp::self(), &ScreenLocker::KSldApp::lockStateChanged, this, diff --git a/src/scene.cpp b/src/scene.cpp index b65fb7cd14..556c644c5e 100644 --- a/src/scene.cpp +++ b/src/scene.cpp @@ -107,6 +107,23 @@ void Scene::initialize() connect(workspace(), &Workspace::geometryChanged, this, [this]() { setGeometry(workspace()->geometry()); }); + + connect(Cursors::self(), &Cursors::currentCursorChanged, this, &Scene::addCursorRepaints); + connect(Cursors::self(), &Cursors::positionChanged, this, &Scene::addCursorRepaints); +} + +void Scene::addCursorRepaints() +{ + const auto outputs = kwinApp()->platform()->enabledOutputs(); + QRegion repaintRegion = Cursors::self()->currentCursor()->geometry(); + repaintRegion |= m_lastCursorGeometry; + for (const auto &output : outputs) { + auto intersection = repaintRegion.intersected(output->geometry()); + if (!intersection.isEmpty() && output->usesSoftwareCursor()) { + addRepaint(intersection); + } + } + m_lastCursorGeometry = Cursors::self()->currentCursor()->geometry(); } void Scene::addRepaintFull() diff --git a/src/scene.h b/src/scene.h index 015001d4b8..af07982bb0 100644 --- a/src/scene.h +++ b/src/scene.h @@ -207,7 +207,7 @@ protected: QRegion *updateRegion, QRegion *validRegion, RenderLoop *renderLoop, const QMatrix4x4 &projection = QMatrix4x4()); // Render cursor texture in case hardware cursor is disabled/non-applicable - virtual void paintCursor(const QRegion ®ion) = 0; + virtual void paintCursor(AbstractOutput *output, const QRegion ®ion) = 0; friend class EffectsHandlerImpl; // called after all effects had their paintScreen() called void finalPaintScreen(int mask, const QRegion ®ion, ScreenPaintData& data); @@ -261,12 +261,15 @@ protected: QVector< Window* > stacking_order; private: void removeRepaints(AbstractOutput *output); + void addCursorRepaints(); + std::chrono::milliseconds m_expectedPresentTimestamp = std::chrono::milliseconds::zero(); QHash< Toplevel*, Window* > m_windows; QMap m_repaints; QRect m_geometry; // how many times finalPaintScreen() has been called int m_paintScreenCount = 0; + QRect m_lastCursorGeometry; }; // The base class for windows representations in composite backends diff --git a/src/scenes/opengl/scene_opengl.cpp b/src/scenes/opengl/scene_opengl.cpp index 1a86f1d9e2..6c57d93a64 100644 --- a/src/scenes/opengl/scene_opengl.cpp +++ b/src/scenes/opengl/scene_opengl.cpp @@ -151,14 +151,14 @@ void SceneOpenGL::handleGraphicsReset(GLenum status) * Render cursor texture in case hardware cursor is disabled. * Useful for screen recording apps or backends that can't do planes. */ -void SceneOpenGL::paintCursor(const QRegion &rendered) +void SceneOpenGL::paintCursor(AbstractOutput *output, const QRegion &rendered) { Cursor* cursor = Cursors::self()->currentCursor(); // don't paint if we use hardware cursor or the cursor is hidden - if (!kwinApp()->platform()->usesSoftwareCursor() || - kwinApp()->platform()->isCursorHidden() || - cursor->image().isNull()) { + if (!output || !output->usesSoftwareCursor() + || Cursors::self()->isCursorHidden() + || cursor->image().isNull()) { return; } @@ -316,7 +316,7 @@ void SceneOpenGL::paint(AbstractOutput *output, const QRegion &damage, const QLi paintScreen(damage.intersected(geo), repaint, &update, &valid, renderLoop, projectionMatrix()); // call generic implementation - paintCursor(valid); + paintCursor(output, valid); if (!GLPlatform::instance()->isGLES() && !output) { const QRegion displayRegion(geometry()); diff --git a/src/scenes/opengl/scene_opengl.h b/src/scenes/opengl/scene_opengl.h index 49b42b9ff6..161558f492 100644 --- a/src/scenes/opengl/scene_opengl.h +++ b/src/scenes/opengl/scene_opengl.h @@ -71,7 +71,7 @@ protected: void paintGenericScreen(int mask, const ScreenPaintData &data) override; Scene::Window *createWindow(Toplevel *t) override; void finalDrawWindow(EffectWindowImpl* w, int mask, const QRegion ®ion, WindowPaintData& data) override; - void paintCursor(const QRegion ®ion) override; + void paintCursor(AbstractOutput *output, const QRegion ®ion) override; private: void doPaintBackground(const QVector< float >& vertices); diff --git a/src/scenes/qpainter/scene_qpainter.cpp b/src/scenes/qpainter/scene_qpainter.cpp index ebca131187..2eb7a46f18 100644 --- a/src/scenes/qpainter/scene_qpainter.cpp +++ b/src/scenes/qpainter/scene_qpainter.cpp @@ -87,7 +87,7 @@ void SceneQPainter::paint(AbstractOutput *output, const QRegion &damage, const Q QRegion updateRegion, validRegion; paintScreen(damage.intersected(geometry), repaint, &updateRegion, &validRegion, renderLoop); - paintCursor(updateRegion); + paintCursor(output, updateRegion); m_painter->end(); renderLoop->endFrame(); @@ -105,9 +105,9 @@ void SceneQPainter::paintBackground(const QRegion ®ion) } } -void SceneQPainter::paintCursor(const QRegion &rendered) +void SceneQPainter::paintCursor(AbstractOutput *output, const QRegion &rendered) { - if (!kwinApp()->platform()->usesSoftwareCursor()) { + if (!output || output->usesSoftwareCursor() || Cursors::self()->isCursorHidden()) { return; } diff --git a/src/scenes/qpainter/scene_qpainter.h b/src/scenes/qpainter/scene_qpainter.h index f71bf0535d..03f1d28c04 100644 --- a/src/scenes/qpainter/scene_qpainter.h +++ b/src/scenes/qpainter/scene_qpainter.h @@ -49,7 +49,7 @@ public: protected: void paintBackground(const QRegion ®ion) override; Scene::Window *createWindow(Toplevel *toplevel) override; - void paintCursor(const QRegion ®ion) override; + void paintCursor(AbstractOutput *output, const QRegion ®ion) override; void paintOffscreenQuickView(OffscreenQuickView *w) override; private: