From 4599a7afab2f912b747e8f2d890be37fbf2d9403 Mon Sep 17 00:00:00 2001 From: wheaney <42350981+wheaney@users.noreply.github.com> Date: Mon, 7 Jul 2025 15:47:31 -0700 Subject: [PATCH] Working cursor mirroring --- kwin/src/breezydesktopeffect.cpp | 102 ++++++++++++------------------- kwin/src/breezydesktopeffect.h | 21 ++++--- kwin/src/qml/DesktopView.qml | 14 +++++ 3 files changed, 66 insertions(+), 71 deletions(-) diff --git a/kwin/src/breezydesktopeffect.cpp b/kwin/src/breezydesktopeffect.cpp index 027fef4..5568547 100644 --- a/kwin/src/breezydesktopeffect.cpp +++ b/kwin/src/breezydesktopeffect.cpp @@ -13,6 +13,7 @@ #include #include +#include Q_LOGGING_CATEGORY(KWIN_XR, "kwin.xr") @@ -47,6 +48,9 @@ BreezyDesktopEffect::BreezyDesktopEffect() } }); + connect(effects, &EffectsHandler::cursorShapeChanged, this, &BreezyDesktopEffect::updateCursorImage); + updateCursorImage(); + setSource(QUrl::fromLocalFile(QStandardPaths::locate(QStandardPaths::GenericDataLocation, QStringLiteral("kwin/effects/breezy_desktop/qml/main.qml")))); m_xrRotationTimer = new QTimer(this); @@ -54,14 +58,17 @@ BreezyDesktopEffect::BreezyDesktopEffect() connect(m_xrRotationTimer, &QTimer::timeout, this, &BreezyDesktopEffect::updateXrRotation); m_xrRotationTimer->start(); - toggle(); + m_cursorUpdateTimer = new QTimer(this); + connect(m_cursorUpdateTimer, &QTimer::timeout, this, &BreezyDesktopEffect::updateCursorPos); + m_cursorUpdateTimer->setInterval(16); // ~60Hz + m_cursorUpdateTimer->start(); } QVariantMap BreezyDesktopEffect::initialProperties(Output *screen) { return QVariantMap{ {QStringLiteral("effect"), QVariant::fromValue(this)}, - {QStringLiteral("targetScreen"), QVariant::fromValue(screen)}, + {QStringLiteral("targetScreen"), QVariant::fromValue(screen)} }; } @@ -111,6 +118,8 @@ void BreezyDesktopEffect::deactivate() } m_shutdownTimer->start(animationDuration()); + disconnect(effects, &EffectsHandler::cursorShapeChanged, this, &BreezyDesktopEffect::updateCursorImage); + m_cursorUpdateTimer->stop(); showCursor(); } @@ -239,89 +248,58 @@ void BreezyDesktopEffect::updateXrRotation() { } } -// show and hide cursor logic pulled from ZoomEffect -GLTexture *BreezyDesktopEffect::ensureCursorTexture() +QString BreezyDesktopEffect::cursorImageSource() const { - if (!m_cursorTexture || m_cursorTextureDirty) { - m_cursorTexture.reset(); - m_cursorTextureDirty = false; - const auto cursor = effects->cursorImage(); - if (!cursor.image().isNull()) { - m_cursorTexture = GLTexture::upload(cursor.image()); - if (!m_cursorTexture) { - return nullptr; - } - m_cursorTexture->setWrapMode(GL_CLAMP_TO_EDGE); - } - } - return m_cursorTexture.get(); + return m_cursorImageSource; } -void BreezyDesktopEffect::markCursorTextureDirty() +QPointF BreezyDesktopEffect::cursorPos() const { - m_cursorTextureDirty = true; + return m_cursorPos; } void BreezyDesktopEffect::showCursor() { if (m_isMouseHidden) { - disconnect(effects, &EffectsHandler::cursorShapeChanged, this, &BreezyDesktopEffect::markCursorTextureDirty); - // show the previously hidden mouse-pointer again and free the loaded texture/picture. effects->showCursor(); - m_cursorTexture.reset(); m_isMouseHidden = false; } } void BreezyDesktopEffect::hideCursor() { - qCCritical(KWIN_XR) << "\t\t\tBreezy - hide cursor"; if (!m_isMouseHidden) { - qCCritical(KWIN_XR) << "\t\t\tBreezy - hide cursor - ensure cursor texture"; - // try to load the cursor-theme into a OpenGL texture and if successful then hide the mouse-pointer - GLTexture *texture = nullptr; - if (effects->isOpenGLCompositing()) { - qCCritical(KWIN_XR) << "\t\t\tBreezy - hide cursor - OpenGL compositing"; - texture = ensureCursorTexture(); - } - if (texture) { - qCCritical(KWIN_XR) << "\t\t\tBreezy - hide cursor - hiding cursor"; - effects->hideCursor(); - connect(effects, &EffectsHandler::cursorShapeChanged, this, &BreezyDesktopEffect::markCursorTextureDirty); - m_isMouseHidden = true; - } + updateCursorImage(); + effects->hideCursor(); + m_isMouseHidden = true; } } -void BreezyDesktopEffect::paintScreen(const RenderTarget &renderTarget, const RenderViewport &viewport, int mask, const QRegion ®ion, Output *screen) -{ - GLTexture *cursorTexture = ensureCursorTexture(); - if (cursorTexture) { - const auto cursor = effects->cursorImage(); - QSizeF cursorSize = QSizeF(cursor.image().size()) / cursor.image().devicePixelRatio(); - - // Ensure cursor size doesn't exceed texture dimensions - const QSize textureSize = cursorTexture->size(); - cursorSize = cursorSize.boundedTo(QSizeF(textureSize)); +void BreezyDesktopEffect::updateCursorImage() +{ + const auto cursor = effects->cursorImage(); + if (!cursor.image().isNull()) { + QByteArray data; + QBuffer buffer(&data); + buffer.open(QIODevice::WriteOnly); + cursor.image().save(&buffer, "PNG"); - const QPointF p = (effects->cursorPos() - cursor.hotSpot()); - - glEnable(GL_BLEND); - glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - auto s = ShaderManager::instance()->pushShader(ShaderTrait::MapTexture | ShaderTrait::TransformColorspace); - s->setColorspaceUniforms(ColorDescription::sRGB, renderTarget.colorDescription(), RenderingIntent::Perceptual); - QMatrix4x4 mvp = viewport.projectionMatrix(); - mvp.translate(p.x(), p.y()); - s->setUniform(GLShader::Mat4Uniform::ModelViewProjectionMatrix, mvp); - cursorTexture->render(cursorSize); - ShaderManager::instance()->popShader(); - glDisable(GL_BLEND); + m_cursorImageSource = QStringLiteral("data:image/png;base64,%1").arg(QString::fromLatin1(data.toBase64())); + } else { + m_cursorImageSource = QString(); } - QuickSceneEffect::paintScreen(renderTarget, viewport, mask, region, screen); + Q_EMIT cursorImageChanged(); } - - -} // namespace KWin +void BreezyDesktopEffect::updateCursorPos() +{ + // Update cursor position from effects + QPointF newPos = effects->cursorPos(); + if (m_cursorPos != newPos) { + m_cursorPos = newPos; + Q_EMIT cursorPosChanged(); + } +} +} #include "moc_breezydesktopeffect.cpp" \ No newline at end of file diff --git a/kwin/src/breezydesktopeffect.h b/kwin/src/breezydesktopeffect.h index c94baf3..3f56426 100644 --- a/kwin/src/breezydesktopeffect.h +++ b/kwin/src/breezydesktopeffect.h @@ -3,14 +3,12 @@ #include #include +#include #include #include namespace KWin { - class GLTexture; - class GLShader; - class BreezyDesktopEffect : public QuickSceneEffect { Q_OBJECT @@ -22,6 +20,8 @@ namespace KWin Q_PROPERTY(BackgroundMode backgroundMode READ backgroundMode NOTIFY backgroundModeChanged) Q_PROPERTY(QColor backgroundColor READ backgroundColor NOTIFY backgroundColorChanged) Q_PROPERTY(QQuaternion xrRotation READ xrRotation NOTIFY xrRotationChanged) + Q_PROPERTY(QString cursorImageSource READ cursorImageSource NOTIFY cursorImageChanged) + Q_PROPERTY(QPointF cursorPos READ cursorPos NOTIFY cursorPosChanged) public: enum class BackgroundMode @@ -33,8 +33,6 @@ namespace KWin BreezyDesktopEffect(); - void paintScreen(const RenderTarget &renderTarget, const RenderViewport &viewport, int mask, const QRegion ®ion, Output *screen) override; - int requestedEffectChainPosition() const override; int animationDuration() const; @@ -44,11 +42,11 @@ namespace KWin bool mouseInvertedY() const; BackgroundMode backgroundMode() const; QColor backgroundColor() const; + QString cursorImageSource() const; + QPointF cursorPos() const; void showCursor(); void hideCursor(); - GLTexture *ensureCursorTexture(); - void markCursorTextureDirty(); QQuaternion xrRotation() const; @@ -57,6 +55,8 @@ namespace KWin void deactivate(); void toggle(); void updateXrRotation(); + void updateCursorImage(); + void updateCursorPos(); Q_SIGNALS: void faceDisplacementChanged(); @@ -68,6 +68,8 @@ namespace KWin void backgroundModeChanged(); void backgroundColorChanged(); void xrRotationChanged(); + void cursorImageChanged(); + void cursorPosChanged(); protected: QVariantMap initialProperties(Output *screen) override; @@ -80,12 +82,13 @@ namespace KWin QList m_toggleShortcut; QList m_borderActivate; QList m_touchBorderActivate; - std::unique_ptr m_cursorTexture; - bool m_cursorTextureDirty = false; + QString m_cursorImageSource; bool m_isMouseHidden = false; QQuaternion m_xrRotation; QTimer *m_xrRotationTimer = nullptr; + QPointF m_cursorPos; + QTimer *m_cursorUpdateTimer = nullptr; }; } // namespace KWin diff --git a/kwin/src/qml/DesktopView.qml b/kwin/src/qml/DesktopView.qml index f87f7cd..d0124a7 100644 --- a/kwin/src/qml/DesktopView.qml +++ b/kwin/src/qml/DesktopView.qml @@ -22,4 +22,18 @@ Item { visible: !model.window.minimized } } + + Image { + id: cursorImg + source: effect.cursorImageSource + cache: false + visible: true // TODO - cursor position bounds check? + x: effect.cursorPos.x - desktopView.screen.geometry.x + y: effect.cursorPos.y - desktopView.screen.geometry.y + z: 9999 // ensure on top + anchors.centerIn: undefined + + layer.enabled: true + layer.smooth: true + } }