From 40475736c1f5455eaebe3a66c56620584becbdf9 Mon Sep 17 00:00:00 2001 From: wheaney <42350981+wheaney@users.noreply.github.com> Date: Thu, 11 Sep 2025 11:46:47 -0700 Subject: [PATCH] Add support for enabling/disabling physical screen mirroring Option to turn physical displays off not implemented yet --- kwin/src/breezydesktopconfig.kcfg | 7 +++++ kwin/src/breezydesktopeffect.cpp | 6 +++++ kwin/src/breezydesktopeffect.h | 4 +++ kwin/src/kcm/breezydesktopeffectkcm.cpp | 2 ++ kwin/src/kcm/breezydesktopeffectkcm.ui | 34 ++++++++++++++++++++++--- kwin/src/qml/BreezyDesktop.qml | 25 ++++++++++++++---- kwin/src/qml/CameraController.qml | 7 ++--- kwin/src/qml/Displays.qml | 2 +- kwin/src/qml/main.qml | 15 ++++++----- 9 files changed, 81 insertions(+), 21 deletions(-) diff --git a/kwin/src/breezydesktopconfig.kcfg b/kwin/src/breezydesktopconfig.kcfg index 2a8ec8b..17d6b9a 100644 --- a/kwin/src/breezydesktopconfig.kcfg +++ b/kwin/src/breezydesktopconfig.kcfg @@ -57,6 +57,13 @@ 0=None, 1=Medium, 2=High, 3=Very High + + 1 + 0 + 2 + + How to handle the physical (built-in) monitors: 0=Off, 1=On - not mirrored in XR, 2=On - mirrored in XR (may impact performance) + true diff --git a/kwin/src/breezydesktopeffect.cpp b/kwin/src/breezydesktopeffect.cpp index e27830d..145b067 100644 --- a/kwin/src/breezydesktopeffect.cpp +++ b/kwin/src/breezydesktopeffect.cpp @@ -203,12 +203,14 @@ void BreezyDesktopEffect::reconfigure(ReconfigureFlags) int wrap = BreezyDesktopConfig::displayWrappingScheme(); int aaQuality = BreezyDesktopConfig::antialiasingQuality(); bool removeVD = BreezyDesktopConfig::removeVirtualDisplaysOnDisable(); + int physDisplaysMode = BreezyDesktopConfig::physicalDisplaysMode(); bool changed = false; if (!qFuzzyCompare(m_displayHorizontalOffset, horiz)) { m_displayHorizontalOffset = horiz; changed = true; } if (!qFuzzyCompare(m_displayVerticalOffset, vert)) { m_displayVerticalOffset = vert; changed = true; } if (m_displayWrappingScheme != wrap) { m_displayWrappingScheme = wrap; Q_EMIT displayWrappingSchemeChanged(); } if (m_antialiasingQuality != aaQuality) { m_antialiasingQuality = aaQuality; Q_EMIT antialiasingQualityChanged(); } if (m_removeVirtualDisplaysOnDisable != removeVD) { m_removeVirtualDisplaysOnDisable = removeVD; Q_EMIT removeVirtualDisplaysOnDisableChanged(); } + if (m_physicalDisplaysMode != physDisplaysMode) { m_physicalDisplaysMode = physDisplaysMode; Q_EMIT physicalDisplaysModeChanged(); } if (changed) Q_EMIT displayOffsetChanged(); } @@ -427,6 +429,10 @@ bool BreezyDesktopEffect::removeVirtualDisplaysOnDisable() const { return m_removeVirtualDisplaysOnDisable; } +int BreezyDesktopEffect::physicalDisplaysMode() const { + return m_physicalDisplaysMode; +} + bool BreezyDesktopEffect::checkParityByte(const char* data) { const uint8_t parityByte = static_cast(data[DataView::IMU_PARITY_BYTE[DataView::OFFSET_INDEX]]); uint8_t parity = 0; diff --git a/kwin/src/breezydesktopeffect.h b/kwin/src/breezydesktopeffect.h index f49dba8..3ac3fbf 100644 --- a/kwin/src/breezydesktopeffect.h +++ b/kwin/src/breezydesktopeffect.h @@ -37,6 +37,7 @@ namespace KWin Q_PROPERTY(bool customBannerEnabled READ customBannerEnabled NOTIFY devicePropertiesChanged) Q_PROPERTY(int antialiasingQuality READ antialiasingQuality NOTIFY antialiasingQualityChanged) Q_PROPERTY(bool removeVirtualDisplaysOnDisable READ removeVirtualDisplaysOnDisable NOTIFY removeVirtualDisplaysOnDisableChanged) + Q_PROPERTY(int physicalDisplaysMode READ physicalDisplaysMode NOTIFY physicalDisplaysModeChanged) public: @@ -75,6 +76,7 @@ namespace KWin bool customBannerEnabled() const; int antialiasingQuality() const; bool removeVirtualDisplaysOnDisable() const; + int physicalDisplaysMode() const; void showCursor(); void hideCursor(); @@ -104,6 +106,7 @@ namespace KWin void devicePropertiesChanged(); void antialiasingQualityChanged(); void removeVirtualDisplaysOnDisableChanged(); + void physicalDisplaysModeChanged(); protected: QVariantMap initialProperties(Output *screen) override; @@ -142,6 +145,7 @@ namespace KWin int m_displayWrappingScheme = 0; // 0=auto,1=horizontal,2=vertical,3=flat int m_antialiasingQuality = 3; // 0=None, 1=Medium, 2=High, 3=VeryHigh bool m_removeVirtualDisplaysOnDisable = true; + int m_physicalDisplaysMode = 1; // 0=Off,1=On no mirror,2=On mirrored QList m_virtualOutputs; }; diff --git a/kwin/src/kcm/breezydesktopeffectkcm.cpp b/kwin/src/kcm/breezydesktopeffectkcm.cpp index a3b62e4..14cf70b 100644 --- a/kwin/src/kcm/breezydesktopeffectkcm.cpp +++ b/kwin/src/kcm/breezydesktopeffectkcm.cpp @@ -106,6 +106,7 @@ BreezyDesktopEffectConfig::BreezyDesktopEffectConfig(QObject *parent, const KPlu connect(ui.kcfg_DisplayVerticalOffset, &QSlider::valueChanged, this, &BreezyDesktopEffectConfig::save); connect(ui.kcfg_DisplayWrappingScheme, qOverload(&QComboBox::currentIndexChanged), this, &BreezyDesktopEffectConfig::save); connect(ui.kcfg_AntialiasingQuality, qOverload(&QComboBox::currentIndexChanged), this, &BreezyDesktopEffectConfig::save); + connect(ui.kcfg_PhysicalDisplaysMode, qOverload(&QComboBox::currentIndexChanged), this, &BreezyDesktopEffectConfig::save); connect(ui.kcfg_RemoveVirtualDisplaysOnDisable, &QCheckBox::toggled, this, &BreezyDesktopEffectConfig::save); if (auto label = widget()->findChild("labelAppNameVersion")) { @@ -226,6 +227,7 @@ void BreezyDesktopEffectConfig::updateUiFromConfig() ui.kcfg_DisplayVerticalOffset->setValue(BreezyDesktopConfig::self()->displayVerticalOffset()); ui.kcfg_DisplayWrappingScheme->setCurrentIndex(BreezyDesktopConfig::self()->displayWrappingScheme()); ui.kcfg_AntialiasingQuality->setCurrentIndex(BreezyDesktopConfig::self()->antialiasingQuality()); + ui.kcfg_PhysicalDisplaysMode->setCurrentIndex(BreezyDesktopConfig::self()->physicalDisplaysMode()); ui.kcfg_RemoveVirtualDisplaysOnDisable->setChecked(BreezyDesktopConfig::self()->removeVirtualDisplaysOnDisable()); ui.kcfg_ZoomOnFocusEnabled->setChecked(BreezyDesktopConfig::self()->zoomOnFocusEnabled()); ui.kcfg_FocusedDisplayDistance->setEnabled(ui.kcfg_ZoomOnFocusEnabled->isChecked()); diff --git a/kwin/src/kcm/breezydesktopeffectkcm.ui b/kwin/src/kcm/breezydesktopeffectkcm.ui index 1b57da3..d91049f 100644 --- a/kwin/src/kcm/breezydesktopeffectkcm.ui +++ b/kwin/src/kcm/breezydesktopeffectkcm.ui @@ -267,13 +267,39 @@ + + + Physical Displays On/Off: + + + + + + + + Off + + + + + On - not mirrored in XR + + + + + On - mirrored in XR (may impact performance) + + + + + Display Horizontal Offset: - + 2 @@ -292,14 +318,14 @@ - + Display Vertical Offset: - + 2 @@ -318,7 +344,7 @@ - + false diff --git a/kwin/src/qml/BreezyDesktop.qml b/kwin/src/qml/BreezyDesktop.qml index 4520c1e..7409a87 100644 --- a/kwin/src/qml/BreezyDesktop.qml +++ b/kwin/src/qml/BreezyDesktop.qml @@ -6,9 +6,9 @@ Node { id: breezyDesktop property var viewportResolution: effect.displayResolution - property var screens: root.screens - property var fovDetails: root.fovDetails - property var monitorPlacements: root.monitorPlacements + required property var screens + required property var fovDetails + required property var monitorPlacements property int focusedMonitorIndex: -1 Displays { @@ -31,8 +31,8 @@ Node { 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 real screenRotationY: displays.radianToDegree(monitorPlacement?.rotationAngleRadians.y ?? 0) + property real screenRotationX: displays.radianToDegree(monitorPlacement?.rotationAngleRadians.x ?? 0) property matrix4x4 rotationMatrix: { const matrix = Qt.matrix4x4(); matrix.rotate(screenRotationY, Qt.vector3d(0, 1, 0)); @@ -51,6 +51,8 @@ Node { eulerRotation.y: screenRotationY eulerRotation.x: screenRotationX position: { + if (!monitorPlacement) return Qt.vector3d(0, 0, 0); + const displayNwu = monitorPlacement.centerNoRotate .times(monitorDistance / effect.allDisplaysDistance); @@ -112,6 +114,19 @@ Node { } } + // release references to displays and stale indexes + onScreensChanged: { + breezyDesktop.focusedMonitorIndex = -1; + zoomOutAnimation.stop(); + zoomInAnimation.stop(); + zoomOnFocusSequence.stop(); + + zoomOutAnimation.target = null; + zoomInAnimation.target = null; + zoomOutSeqAnimation.target = null; + zoomInSeqAnimation.target = null; + } + NumberAnimation { id: zoomOutAnimation property: "monitorDistance" diff --git a/kwin/src/qml/CameraController.qml b/kwin/src/qml/CameraController.qml index 0b7eba6..6d0b2d6 100644 --- a/kwin/src/qml/CameraController.qml +++ b/kwin/src/qml/CameraController.qml @@ -2,7 +2,7 @@ import QtQuick import QtQuick3D Item { - id: root + id: cameraController required property Camera camera required property var fovDetails @@ -13,9 +13,6 @@ Item { property bool sbsEnabled: effect.sbsEnabled property bool customBannerEnabled: effect.customBannerEnabled - implicitWidth: parent.width - implicitHeight: parent.height - Displays { id: displays } @@ -63,7 +60,7 @@ Item { function updateFOV() { const aspectRatio = displayResolution[0] / displayResolution[1]; camera.fieldOfView = displays.radianToDegree(displays.diagonalToCrossFOVs( - displays.degreeToRadian(root.diagonalFOV), + displays.degreeToRadian(cameraController.diagonalFOV), aspectRatio ).vertical); } diff --git a/kwin/src/qml/Displays.qml b/kwin/src/qml/Displays.qml index dee70b6..0e7f069 100644 --- a/kwin/src/qml/Displays.qml +++ b/kwin/src/qml/Displays.qml @@ -48,7 +48,7 @@ QtObject { } } - function fovDetails(screens, viewportWidth, viewportHeight, viewportDiagonalFOV, lensDistanceRatio, defaultDisplayDistance, wrappingChoice) { + function buildFovDetails(screens, viewportWidth, viewportHeight, viewportDiagonalFOV, lensDistanceRatio, defaultDisplayDistance, wrappingChoice) { const aspect = viewportWidth / viewportHeight; const fovRadians = diagonalToCrossFOVs(degreeToRadian(viewportDiagonalFOV), aspect); const defaultDistanceVerticalRadians = 2 * Math.atan(Math.tan(fovRadians.vertical / 2) / defaultDisplayDistance); diff --git a/kwin/src/qml/main.qml b/kwin/src/qml/main.qml index 5be9f5c..8e54650 100644 --- a/kwin/src/qml/main.qml +++ b/kwin/src/qml/main.qml @@ -25,10 +25,10 @@ Item { property real viewportDiagonalFOVDegrees: effect.diagonalFOV property var viewportResolution: effect.displayResolution - property var screens: KWinComponents.Workspace.screens - // .filter(function(screen) { - // return supportedModels.includes(screen.model); - // }) + property bool mirrorPhysicalDisplays: effect.physicalDisplaysMode === 2 + property var screens: KWinComponents.Workspace.screens.filter(function(screen) { + return mirrorPhysicalDisplays || screen.name.includes("BreezyDesktop") || supportedModels.some(model => screen.model.includes(model)); + }) // x value for placing the viewport in the middle of all screens property real screensXMid: { @@ -62,7 +62,7 @@ Item { id: displays } - property var fovDetails: displays.fovDetails( + property var fovDetails: displays.buildFovDetails( screens, viewportResolution[0], viewportResolution[1], @@ -116,13 +116,16 @@ Item { BreezyDesktop { id: breezyDesktop + screens: root.screens + fovDetails: root.fovDetails + monitorPlacements: root.monitorPlacements } CameraController { id: cameraController anchors.fill: parent camera: camera - fovDetails: fovDetails + fovDetails: root.fovDetails } } }