diff --git a/kwin/src/CMakeLists.txt b/kwin/src/CMakeLists.txt index 5b88fdd..8cbb15a 100644 --- a/kwin/src/CMakeLists.txt +++ b/kwin/src/CMakeLists.txt @@ -67,4 +67,6 @@ target_link_libraries(breezy_desktop ) -install(DIRECTORY qml DESTINATION ${KDE_INSTALL_DATADIR}/kwin/effects/breezy_desktop) \ No newline at end of file +install(DIRECTORY qml DESTINATION ${KDE_INSTALL_DATADIR}/kwin/effects/breezy_desktop) +install(FILES qml/cursorOverlay.frag DESTINATION ${KDE_INSTALL_DATADIR}/kwin/effects/breezy_desktop/qml) +install(FILES qml/cursorOverlay.vert DESTINATION ${KDE_INSTALL_DATADIR}/kwin/effects/breezy_desktop/qml) \ No newline at end of file diff --git a/kwin/src/breezydesktopeffect.cpp b/kwin/src/breezydesktopeffect.cpp index fbe5221..55ecdc4 100644 --- a/kwin/src/breezydesktopeffect.cpp +++ b/kwin/src/breezydesktopeffect.cpp @@ -540,6 +540,11 @@ QString BreezyDesktopEffect::cursorImageSource() const return m_cursorImageSource; } +QSize BreezyDesktopEffect::cursorImageSize() const +{ + return m_cursorImageSize; +} + QPointF BreezyDesktopEffect::cursorPos() const { return m_cursorPos; @@ -566,10 +571,12 @@ void BreezyDesktopEffect::updateCursorImage() cursor.image().save(&buffer, "PNG"); m_cursorImageSource = QStringLiteral("data:image/png;base64,%1").arg(QString::fromLatin1(data.toBase64())); + m_cursorImageSize = cursor.image().size(); } else { m_cursorImageSource = QString(); + m_cursorImageSize = QSize(); } - Q_EMIT cursorImageChanged(); + Q_EMIT cursorImageSourceChanged(); } void BreezyDesktopEffect::updateCursorPos() diff --git a/kwin/src/breezydesktopeffect.h b/kwin/src/breezydesktopeffect.h index 6011c42..4d21cb6 100644 --- a/kwin/src/breezydesktopeffect.h +++ b/kwin/src/breezydesktopeffect.h @@ -16,11 +16,12 @@ namespace KWin Q_OBJECT Q_PROPERTY(bool isEnabled READ isEnabled NOTIFY enabledStateChanged) Q_PROPERTY(bool zoomOnFocusEnabled READ isZoomOnFocusEnabled WRITE setZoomOnFocusEnabled NOTIFY zoomOnFocusChanged) - Q_PROPERTY(bool imuResetState READ imuResetState NOTIFY imuRotationsChanged) - Q_PROPERTY(QList imuRotations READ imuRotations NOTIFY imuRotationsChanged) - Q_PROPERTY(quint32 imuTimeElapsedMs READ imuTimeElapsedMs NOTIFY imuRotationsChanged) - Q_PROPERTY(quint64 imuTimestamp READ imuTimestamp NOTIFY imuRotationsChanged) - Q_PROPERTY(QString cursorImageSource READ cursorImageSource NOTIFY cursorImageChanged) + Q_PROPERTY(bool imuResetState READ imuResetState) + Q_PROPERTY(QList imuRotations READ imuRotations) + Q_PROPERTY(quint32 imuTimeElapsedMs READ imuTimeElapsedMs) + Q_PROPERTY(quint64 imuTimestamp READ imuTimestamp) + Q_PROPERTY(QString cursorImageSource READ cursorImageSource NOTIFY cursorImageSourceChanged) + Q_PROPERTY(QSize cursorImageSize READ cursorImageSize NOTIFY cursorImageSourceChanged) Q_PROPERTY(QPointF cursorPos READ cursorPos NOTIFY cursorPosChanged) Q_PROPERTY(QList lookAheadConfig READ lookAheadConfig NOTIFY devicePropertiesChanged) Q_PROPERTY(QList displayResolution READ displayResolution NOTIFY devicePropertiesChanged) @@ -44,6 +45,7 @@ namespace KWin int requestedEffectChainPosition() const override; QString cursorImageSource() const; + QSize cursorImageSize() const; QPointF cursorPos() const; bool isEnabled() const; @@ -91,7 +93,7 @@ namespace KWin void enabledStateChanged(); void zoomOnFocusChanged(); void imuRotationsChanged(); - void cursorImageChanged(); + void cursorImageSourceChanged(); void cursorPosChanged(); void devicePropertiesChanged(); @@ -107,6 +109,7 @@ namespace KWin QTimer *m_shutdownTimer; QString m_cursorImageSource; + QSize m_cursorImageSize; bool m_enabled = false; bool m_zoomOnFocusEnabled = false; diff --git a/kwin/src/qml/BreezyDesktop.qml b/kwin/src/qml/BreezyDesktop.qml index 68fa21c..4520c1e 100644 --- a/kwin/src/qml/BreezyDesktop.qml +++ b/kwin/src/qml/BreezyDesktop.qml @@ -9,7 +9,6 @@ Node { property var screens: root.screens property var fovDetails: root.fovDetails property var monitorPlacements: root.monitorPlacements - property var imuRotations: effect.imuRotations property int focusedMonitorIndex: -1 Displays { @@ -29,10 +28,17 @@ Node { delegate: BreezyDesktopDisplay { screen: breezyDesktop.screens[index] monitorPlacement: breezyDesktop.monitorPlacements[index] + property real monitorDistance: effect.allDisplaysDistance property real targetDistance: effect.allDisplaysDistance property real screenRotationY: displays.radianToDegree(monitorPlacement.rotationAngleRadians.y) property real screenRotationX: displays.radianToDegree(monitorPlacement.rotationAngleRadians.x) + property matrix4x4 rotationMatrix: { + const matrix = Qt.matrix4x4(); + matrix.rotate(screenRotationY, Qt.vector3d(0, 1, 0)); + matrix.rotate(screenRotationX, Qt.vector3d(1, 0, 0)); + return matrix; + } property vector3d screenScale: { const geometry = screen.geometry; @@ -45,19 +51,12 @@ Node { eulerRotation.y: screenRotationY eulerRotation.x: screenRotationX position: { - // camera looks along the negative Z axis - const positionVector = - displays.nwuToEusVector(monitorPlacement.centerNoRotate) - .times(monitorDistance / effect.allDisplaysDistance); + const displayNwu = + monitorPlacement.centerNoRotate + .times(monitorDistance / effect.allDisplaysDistance); - // position vector is only translated in flat directions, without rotations applied, so apply them here - const rotationMatrix = Qt.matrix4x4(); - // only one of these should ever be non-zero, since we only rotate in the direction of the "wrap" preference - rotationMatrix.rotate(screenRotationY, Qt.vector3d(0, 1, 0)); - rotationMatrix.rotate(screenRotationX, Qt.vector3d(1, 0, 0)); - - return rotationMatrix.times(positionVector); + return rotationMatrix.times(displays.nwuToEusVector(displayNwu)); } } } @@ -67,12 +66,12 @@ Node { repeat: true running: true onTriggered: { - if (breezyDesktop.imuRotations && breezyDesktop.imuRotations.length > 0) { + if (effect.imuRotations && effect.imuRotations.length > 0) { let focusedIndex = -1; if (effect.zoomOnFocusEnabled) { focusedIndex = displays.findFocusedMonitor( - displays.eusToNwuQuat(breezyDesktop.imuRotations[0]), + displays.eusToNwuQuat(effect.imuRotations[0]), breezyDesktop.monitorPlacements.map(monitorVectors => monitorVectors.centerLook), breezyDesktop.focusedMonitorIndex, false, // TODO smooth follow diff --git a/kwin/src/qml/BreezyDesktopDisplay.qml b/kwin/src/qml/BreezyDesktopDisplay.qml index d7ea78a..e192c11 100644 --- a/kwin/src/qml/BreezyDesktopDisplay.qml +++ b/kwin/src/qml/BreezyDesktopDisplay.qml @@ -8,19 +8,46 @@ Model { required property var monitorPlacement required property int index + property string cursorImageSource: effect.cursorImageSource + property size cursorImageSize: effect.cursorImageSize + property point cursorPos: effect.cursorPos + source: "#Rectangle" materials: [ - DefaultMaterial { - cullMode: Material.NoCulling - lighting: DefaultMaterial.NoLighting - depthDrawMode: Material.AlwaysDepthDraw - diffuseMap: Texture { - sourceItem: DesktopView { - screen: display.screen - width: display.screen.geometry.width - height: display.screen.geometry.height + CustomMaterial { + id: customMat + depthDrawMode: CustomMaterial.AlwaysDepthDraw + shadingMode: CustomMaterial.Unshaded + + property real screenWidth: display.screen.geometry.width + property real screenHeight: display.screen.geometry.height + property real cursorX: display.cursorPos.x - display.screen.geometry.x + property real cursorY: display.cursorPos.y - display.screen.geometry.y + property real cursorW: display.cursorImageSize.width + property real cursorH: display.cursorImageSize.height + property bool showCursor: cursorX >= 0 && cursorX < screenWidth && cursorY >= 0 && cursorY < screenHeight + + property TextureInput desktopTex: TextureInput { + texture: Texture { + sourceItem: DesktopView { + screen: display.screen + width: display.screen.geometry.width + height: display.screen.geometry.height + } } } + property TextureInput cursorTex: TextureInput { + texture: Texture { + sourceItem: Image { + source: effect.cursorImageSource + width: effect.cursorImageSize.width + height: effect.cursorImageSize.height + } + } + } + + fragmentShader: "cursorOverlay.frag" + vertexShader: "cursorOverlay.vert" } ] } diff --git a/kwin/src/qml/CameraController.qml b/kwin/src/qml/CameraController.qml index 5cf3853..ce69a9c 100644 --- a/kwin/src/qml/CameraController.qml +++ b/kwin/src/qml/CameraController.qml @@ -6,10 +6,6 @@ Item { required property Camera camera - property var imuRotations: effect.imuRotations - property int imuTimeElapsedMs: effect.imuTimeElapsedMs - property double imuTimestamp: effect.imuTimestamp - property var lookAheadConfig: effect.lookAheadConfig property var displayResolution: effect.displayResolution property real diagonalFOV: effect.diagonalFOV property real lensDistanceRatio: effect.lensDistanceRatio @@ -67,14 +63,18 @@ Item { onDisplayResolutionChanged: updateFOV(); onDiagonalFOVChanged: updateFOV(); - onImuRotationsChanged: { - if (root.imuRotations && root.imuRotations.length > 0) { - updateCamera(applyLookAhead( - root.imuRotations[0], - root.imuRotations[1], - root.imuTimeElapsedMs, - lookAheadMS(root.imuTimestamp, root.lookAheadConfig, -1) - )); + + FrameAnimation { + running: true + onTriggered: { + if (effect.imuRotations && effect.imuRotations.length > 0) { + updateCamera(applyLookAhead( + effect.imuRotations[0], + effect.imuRotations[1], + effect.imuTimeElapsedMs, + lookAheadMS(effect.imuTimestamp, effect.lookAheadConfig, -1) + )); + } } } } diff --git a/kwin/src/qml/DesktopView.qml b/kwin/src/qml/DesktopView.qml index 03cacd6..10d9940 100644 --- a/kwin/src/qml/DesktopView.qml +++ b/kwin/src/qml/DesktopView.qml @@ -40,18 +40,4 @@ Item { visible: onThisScreen && !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 - } } diff --git a/kwin/src/qml/SingleDesktopView.qml b/kwin/src/qml/SingleDesktopView.qml new file mode 100644 index 0000000..3d0151f --- /dev/null +++ b/kwin/src/qml/SingleDesktopView.qml @@ -0,0 +1,43 @@ +import QtQuick + +Item { + id: singleDesktopView + property point cursorPos: effect.cursorPos + + function cursorInBounds() { + const x = cursorPos.x + const y = cursorPos.y + const screenGeom = targetScreen.geometry + return x >= screenGeom.x && + x < screenGeom.x + screenGeom.width && + y >= screenGeom.y && + y < screenGeom.y + screenGeom.height + } + + DesktopView { + screen: targetScreen + width: targetScreen.geometry.width + height: targetScreen.geometry.height + } + + Image { + id: cursorImg + x: 0 + y: 0 + z: 9999 // ensure on top + } + + onCursorPosChanged: { + if (singleDesktopView.cursorInBounds()) { + const newX = effect.cursorPos.x - targetScreen.geometry.x + const newY = effect.cursorPos.y - targetScreen.geometry.y + const newSrc = effect.cursorImageSource + if (cursorImg.x !== newX) cursorImg.x = newX + if (cursorImg.y !== newY) cursorImg.y = newY + if (cursorImg.source !== newSrc) cursorImg.source = newSrc + if (!cursorImg.visible) cursorImg.visible = true + } else if (cursorImg.visible) { + cursorImg.visible = false + } + } +} \ No newline at end of file diff --git a/kwin/src/qml/cursorOverlay.frag b/kwin/src/qml/cursorOverlay.frag new file mode 100644 index 0000000..4660e60 --- /dev/null +++ b/kwin/src/qml/cursorOverlay.frag @@ -0,0 +1,18 @@ +VARYING vec3 pos; +VARYING vec2 texcoord; + +void MAIN() { + vec2 tex = vec2(texcoord.x, 1.0 - texcoord.y); + vec4 color = texture(desktopTex, tex); + if (showCursor) { + vec2 fragCoord = tex * vec2(screenWidth, screenHeight); + vec2 cursorTopLeft = vec2(cursorX, cursorY); + vec2 cursorBottomRight = cursorTopLeft + vec2(cursorW, cursorH); + if (fragCoord.x >= cursorTopLeft.x && fragCoord.x < cursorBottomRight.x && fragCoord.y >= cursorTopLeft.y && fragCoord.y < cursorBottomRight.y) { + vec2 rel = (fragCoord - cursorTopLeft) / vec2(cursorW, cursorH); + vec4 cursorCol = texture(cursorTex, rel); + color = mix(color, cursorCol, cursorCol.a); + } + } + FRAGCOLOR = color; +} \ No newline at end of file diff --git a/kwin/src/qml/cursorOverlay.vert b/kwin/src/qml/cursorOverlay.vert new file mode 100644 index 0000000..4dc6bf3 --- /dev/null +++ b/kwin/src/qml/cursorOverlay.vert @@ -0,0 +1,10 @@ +VARYING vec3 pos; +VARYING vec2 texcoord; + +// this is a no-op vertex shader, CustomMaterial required one +void MAIN() +{ + pos = VERTEX; + texcoord = UV0; + POSITION = MODELVIEWPROJECTION_MATRIX * vec4(pos, 1.0); +} \ No newline at end of file diff --git a/kwin/src/qml/main.qml b/kwin/src/qml/main.qml index 6b9a615..01cd949 100644 --- a/kwin/src/qml/main.qml +++ b/kwin/src/qml/main.qml @@ -89,11 +89,7 @@ Item { Component { id: desktopViewComponent - DesktopView { - screen: root.targetScreen - width: root.targetScreen.geometry.width - height: root.targetScreen.geometry.height - } + SingleDesktopView {} } Component {