From bc959c2f4c2a8fdc2e974e4a0c3441e3f7bad2bf Mon Sep 17 00:00:00 2001 From: wheaney <42350981+wheaney@users.noreply.github.com> Date: Tue, 2 Sep 2025 22:01:59 -0700 Subject: [PATCH] Add display offsets and wrapping scheme options --- kwin/src/breezydesktopconfig.kcfg | 21 +++++ kwin/src/breezydesktopeffect.cpp | 20 +++++ kwin/src/breezydesktopeffect.h | 11 +++ kwin/src/kcm/breezydesktopeffectkcm.cpp | 15 ++-- kwin/src/kcm/breezydesktopeffectkcm.ui | 102 +++++++++++++++++++++--- kwin/src/qml/Displays.qml | 13 ++- kwin/src/qml/main.qml | 16 +++- 7 files changed, 172 insertions(+), 26 deletions(-) diff --git a/kwin/src/breezydesktopconfig.kcfg b/kwin/src/breezydesktopconfig.kcfg index 8d0824d..3e47677 100644 --- a/kwin/src/breezydesktopconfig.kcfg +++ b/kwin/src/breezydesktopconfig.kcfg @@ -29,5 +29,26 @@ How far apart the displays are visually (not logically) + + 0 + -250 + 250 + + Horizontal offset as a percent of the viewport width (-2.50 to 2.50) + + + 0 + -250 + 250 + + Vertical offset as a percent of the viewport height (-2.50 to 2.50) + + + 0 + 0 + 3 + + How to arrange monitors: 0=Auto, 1=Horizontal, 2=Vertical, 3=Flat + diff --git a/kwin/src/breezydesktopeffect.cpp b/kwin/src/breezydesktopeffect.cpp index 0ae5596..330ece4 100644 --- a/kwin/src/breezydesktopeffect.cpp +++ b/kwin/src/breezydesktopeffect.cpp @@ -153,6 +153,14 @@ void BreezyDesktopEffect::reconfigure(ReconfigureFlags) setAllDisplaysDistance(BreezyDesktopConfig::allDisplaysDistance() / 100.0f); setDisplaySpacing(BreezyDesktopConfig::displaySpacing() / 1000.0f); setZoomOnFocusEnabled(BreezyDesktopConfig::zoomOnFocusEnabled()); + qreal horiz = BreezyDesktopConfig::displayHorizontalOffset() / 100.0f; + qreal vert = BreezyDesktopConfig::displayVerticalOffset() / 100.0f; + int wrap = BreezyDesktopConfig::displayWrappingScheme(); + 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 (changed) Q_EMIT displayOffsetChanged(); } QVariantMap BreezyDesktopEffect::initialProperties(Output *screen) @@ -327,6 +335,18 @@ void BreezyDesktopEffect::setDisplaySpacing(qreal spacing) { } } +qreal BreezyDesktopEffect::displayHorizontalOffset() const { + return m_displayHorizontalOffset; +} + +qreal BreezyDesktopEffect::displayVerticalOffset() const { + return m_displayVerticalOffset; +} + +int BreezyDesktopEffect::displayWrappingScheme() const { + return m_displayWrappingScheme; +} + qreal BreezyDesktopEffect::diagonalFOV() const { return m_diagonalFOV; } diff --git a/kwin/src/breezydesktopeffect.h b/kwin/src/breezydesktopeffect.h index 4f232b5..6011c42 100644 --- a/kwin/src/breezydesktopeffect.h +++ b/kwin/src/breezydesktopeffect.h @@ -27,6 +27,9 @@ namespace KWin Q_PROPERTY(qreal focusedDisplayDistance READ focusedDisplayDistance NOTIFY focusedDisplayDistanceChanged) Q_PROPERTY(qreal allDisplaysDistance READ allDisplaysDistance NOTIFY allDisplaysDistanceChanged) Q_PROPERTY(qreal displaySpacing READ displaySpacing NOTIFY displaySpacingChanged) + Q_PROPERTY(qreal displayHorizontalOffset READ displayHorizontalOffset NOTIFY displayOffsetChanged) + Q_PROPERTY(qreal displayVerticalOffset READ displayVerticalOffset NOTIFY displayOffsetChanged) + Q_PROPERTY(int displayWrappingScheme READ displayWrappingScheme NOTIFY displayWrappingSchemeChanged) Q_PROPERTY(qreal diagonalFOV READ diagonalFOV NOTIFY devicePropertiesChanged) Q_PROPERTY(qreal lensDistanceRatio READ lensDistanceRatio NOTIFY devicePropertiesChanged) Q_PROPERTY(bool sbsEnabled READ sbsEnabled NOTIFY devicePropertiesChanged) @@ -58,6 +61,9 @@ namespace KWin void setAllDisplaysDistance(qreal distance); qreal displaySpacing() const; void setDisplaySpacing(qreal spacing); + qreal displayHorizontalOffset() const; + qreal displayVerticalOffset() const; + int displayWrappingScheme() const; qreal diagonalFOV() const; qreal lensDistanceRatio() const; bool sbsEnabled() const; @@ -80,6 +86,8 @@ namespace KWin void focusedDisplayDistanceChanged(); void allDisplaysDistanceChanged(); void displaySpacingChanged(); + void displayOffsetChanged(); + void displayWrappingSchemeChanged(); void enabledStateChanged(); void zoomOnFocusChanged(); void imuRotationsChanged(); @@ -119,6 +127,9 @@ namespace KWin qreal m_focusedDisplayDistance = 0.85; qreal m_allDisplaysDistance = 1.05; qreal m_displaySpacing = 0.0; + qreal m_displayHorizontalOffset = 0.0; + qreal m_displayVerticalOffset = 0.0; + int m_displayWrappingScheme = 0; // 0=auto,1=horizontal,2=vertical,3=flat QList m_virtualOutputs; }; diff --git a/kwin/src/kcm/breezydesktopeffectkcm.cpp b/kwin/src/kcm/breezydesktopeffectkcm.cpp index 03cc41f..bc81e1e 100644 --- a/kwin/src/kcm/breezydesktopeffectkcm.cpp +++ b/kwin/src/kcm/breezydesktopeffectkcm.cpp @@ -14,16 +14,13 @@ #include #include -#include -#include -#include +#include #include #include #include #include - -#include -#include +#include +#include Q_LOGGING_CATEGORY(KWIN_XR, "kwin.xr") @@ -80,6 +77,9 @@ BreezyDesktopEffectConfig::BreezyDesktopEffectConfig(QObject *parent, const KPlu connect(ui.kcfg_FocusedDisplayDistance, &QSlider::valueChanged, this, &BreezyDesktopEffectConfig::save); connect(ui.kcfg_AllDisplaysDistance, &QSlider::valueChanged, this, &BreezyDesktopEffectConfig::save); connect(ui.kcfg_DisplaySpacing, &QSlider::valueChanged, this, &BreezyDesktopEffectConfig::save); + connect(ui.kcfg_DisplayHorizontalOffset, &QSlider::valueChanged, this, &BreezyDesktopEffectConfig::save); + connect(ui.kcfg_DisplayVerticalOffset, &QSlider::valueChanged, this, &BreezyDesktopEffectConfig::save); + connect(ui.kcfg_DisplayWrappingScheme, qOverload(&QComboBox::currentIndexChanged), this, &BreezyDesktopEffectConfig::save); if (auto label = widget()->findChild("labelAppNameVersion")) { label->setText(QStringLiteral("Breezy Desktop - v%1").arg(QLatin1String(BREEZY_DESKTOP_VERSION_STR))); @@ -163,6 +163,9 @@ void BreezyDesktopEffectConfig::updateUiFromConfig() ui.kcfg_FocusedDisplayDistance->setValue(BreezyDesktopConfig::self()->focusedDisplayDistance()); ui.kcfg_AllDisplaysDistance->setValue(BreezyDesktopConfig::self()->allDisplaysDistance()); ui.kcfg_DisplaySpacing->setValue(BreezyDesktopConfig::self()->displaySpacing()); + ui.kcfg_DisplayHorizontalOffset->setValue(BreezyDesktopConfig::self()->displayHorizontalOffset()); + ui.kcfg_DisplayVerticalOffset->setValue(BreezyDesktopConfig::self()->displayVerticalOffset()); + ui.kcfg_DisplayWrappingScheme->setCurrentIndex(BreezyDesktopConfig::self()->displayWrappingScheme()); 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 0930c71..a88600d 100644 --- a/kwin/src/kcm/breezydesktopeffectkcm.ui +++ b/kwin/src/kcm/breezydesktopeffectkcm.ui @@ -2,6 +2,9 @@ BreezyDesktopEffectConfig + + 800 + @@ -32,7 +35,7 @@ &General - + @@ -112,7 +115,59 @@ - + + + + Display Horizontal Offset: + + + + + + + 2 + + + QSlider::TicksBelow + + + 50 + + + Qt::Horizontal + + + true + + + + + + + Display Vertical Offset: + + + + + + + 2 + + + QSlider::TicksBelow + + + 50 + + + Qt::Horizontal + + + true + + + + @@ -128,17 +183,38 @@ &Advanced - - - - - Advanced settings will appear here. - - - Qt::AlignCenter - - - + + + + + Display Wrapping Scheme: + + + + + + + + Auto + + + + + Horizontal + + + + + Vertical + + + + + Flat + + + + diff --git a/kwin/src/qml/Displays.qml b/kwin/src/qml/Displays.qml index 53d1dbf..dee70b6 100644 --- a/kwin/src/qml/Displays.qml +++ b/kwin/src/qml/Displays.qml @@ -48,11 +48,11 @@ QtObject { } } - function fovDetails(screens, viewportWidth, viewportHeight, viewportDiagonalFOV, lensDistanceRatio, defaultDisplayDistanace) { + function fovDetails(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) / defaultDisplayDistanace); - const defaultDistanceHorizontalRadians = 2 * Math.atan(Math.tan(fovRadians.horizontal / 2) / defaultDisplayDistanace); + const defaultDistanceVerticalRadians = 2 * Math.atan(Math.tan(fovRadians.vertical / 2) / defaultDisplayDistance); + const defaultDistanceHorizontalRadians = 2 * Math.atan(Math.tan(fovRadians.horizontal / 2) / defaultDisplayDistance); // distance needed for the FOV-sized monitor to fill up the screen const fullScreenDistance = viewportHeight / 2 / Math.tan(fovRadians.vertical / 2); @@ -62,6 +62,11 @@ QtObject { const lensToScreenDistance = viewportHeight / 2 / Math.tan(defaultDistanceVerticalRadians / 2); const completeScreenDistancePixels = lensToScreenDistance + lensDistancePixels; + let monitorWrappingScheme = actualWrapScheme(screens, viewportWidth, viewportHeight); + if (wrappingChoice === 1) monitorWrappingScheme = 'horizontal'; + else if (wrappingChoice === 2) monitorWrappingScheme = 'vertical'; + else if (wrappingChoice === 3) monitorWrappingScheme = 'flat'; + return { widthPixels: viewportWidth, heightPixels: viewportHeight, @@ -69,7 +74,7 @@ QtObject { defaultDistanceHorizontalRadians, lensDistancePixels, completeScreenDistancePixels, - monitorWrappingScheme: actualWrapScheme(screens, viewportWidth, viewportHeight), + monitorWrappingScheme: monitorWrappingScheme, curvedDisplay: false // or true }; } diff --git a/kwin/src/qml/main.qml b/kwin/src/qml/main.qml index 983cc67..6b81a7e 100644 --- a/kwin/src/qml/main.qml +++ b/kwin/src/qml/main.qml @@ -61,14 +61,24 @@ Item { id: displays } - property var fovDetails: displays.fovDetails(screens, viewportResolution[0], viewportResolution[1], viewportDiagonalFOVDegrees, effect.lensDistanceRatio, effect.allDisplaysDistance) + property var fovDetails: displays.fovDetails( + screens, + viewportResolution[0], + viewportResolution[1], + viewportDiagonalFOVDegrees, + effect.lensDistanceRatio, + effect.allDisplaysDistance, + effect.displayWrappingScheme + ) property var monitorPlacements: { + const dx = effect.displayHorizontalOffset * viewportResolution[0]; + const dy = effect.displayVerticalOffset * viewportResolution[1]; const adjustedGeometries = screens.map(screen => { const g = screen.geometry; return { - x: g.x - screensXMid, - y: g.y - screensYMid, + x: g.x - screensXMid + dx, + y: g.y - screensYMid + dy, width: g.width, height: g.height };