Add display offsets and wrapping scheme options

This commit is contained in:
wheaney 2025-09-02 22:01:59 -07:00
parent 6c5f08611e
commit bc959c2f4c
7 changed files with 172 additions and 26 deletions

View File

@ -29,5 +29,26 @@
<label>Display Spacing</label> <label>Display Spacing</label>
<description>How far apart the displays are visually (not logically)</description> <description>How far apart the displays are visually (not logically)</description>
</entry> </entry>
<entry name="DisplayHorizontalOffset" type="Int">
<default>0</default>
<min>-250</min>
<max>250</max>
<label>Display Horizontal Offset</label>
<description>Horizontal offset as a percent of the viewport width (-2.50 to 2.50)</description>
</entry>
<entry name="DisplayVerticalOffset" type="Int">
<default>0</default>
<min>-250</min>
<max>250</max>
<label>Display Vertical Offset</label>
<description>Vertical offset as a percent of the viewport height (-2.50 to 2.50)</description>
</entry>
<entry name="DisplayWrappingScheme" type="Int">
<default>0</default>
<min>0</min>
<max>3</max>
<label>Display Wrapping Scheme</label>
<description>How to arrange monitors: 0=Auto, 1=Horizontal, 2=Vertical, 3=Flat</description>
</entry>
</group> </group>
</kcfg> </kcfg>

View File

@ -153,6 +153,14 @@ void BreezyDesktopEffect::reconfigure(ReconfigureFlags)
setAllDisplaysDistance(BreezyDesktopConfig::allDisplaysDistance() / 100.0f); setAllDisplaysDistance(BreezyDesktopConfig::allDisplaysDistance() / 100.0f);
setDisplaySpacing(BreezyDesktopConfig::displaySpacing() / 1000.0f); setDisplaySpacing(BreezyDesktopConfig::displaySpacing() / 1000.0f);
setZoomOnFocusEnabled(BreezyDesktopConfig::zoomOnFocusEnabled()); 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) 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 { qreal BreezyDesktopEffect::diagonalFOV() const {
return m_diagonalFOV; return m_diagonalFOV;
} }

View File

@ -27,6 +27,9 @@ namespace KWin
Q_PROPERTY(qreal focusedDisplayDistance READ focusedDisplayDistance NOTIFY focusedDisplayDistanceChanged) Q_PROPERTY(qreal focusedDisplayDistance READ focusedDisplayDistance NOTIFY focusedDisplayDistanceChanged)
Q_PROPERTY(qreal allDisplaysDistance READ allDisplaysDistance NOTIFY allDisplaysDistanceChanged) Q_PROPERTY(qreal allDisplaysDistance READ allDisplaysDistance NOTIFY allDisplaysDistanceChanged)
Q_PROPERTY(qreal displaySpacing READ displaySpacing NOTIFY displaySpacingChanged) 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 diagonalFOV READ diagonalFOV NOTIFY devicePropertiesChanged)
Q_PROPERTY(qreal lensDistanceRatio READ lensDistanceRatio NOTIFY devicePropertiesChanged) Q_PROPERTY(qreal lensDistanceRatio READ lensDistanceRatio NOTIFY devicePropertiesChanged)
Q_PROPERTY(bool sbsEnabled READ sbsEnabled NOTIFY devicePropertiesChanged) Q_PROPERTY(bool sbsEnabled READ sbsEnabled NOTIFY devicePropertiesChanged)
@ -58,6 +61,9 @@ namespace KWin
void setAllDisplaysDistance(qreal distance); void setAllDisplaysDistance(qreal distance);
qreal displaySpacing() const; qreal displaySpacing() const;
void setDisplaySpacing(qreal spacing); void setDisplaySpacing(qreal spacing);
qreal displayHorizontalOffset() const;
qreal displayVerticalOffset() const;
int displayWrappingScheme() const;
qreal diagonalFOV() const; qreal diagonalFOV() const;
qreal lensDistanceRatio() const; qreal lensDistanceRatio() const;
bool sbsEnabled() const; bool sbsEnabled() const;
@ -80,6 +86,8 @@ namespace KWin
void focusedDisplayDistanceChanged(); void focusedDisplayDistanceChanged();
void allDisplaysDistanceChanged(); void allDisplaysDistanceChanged();
void displaySpacingChanged(); void displaySpacingChanged();
void displayOffsetChanged();
void displayWrappingSchemeChanged();
void enabledStateChanged(); void enabledStateChanged();
void zoomOnFocusChanged(); void zoomOnFocusChanged();
void imuRotationsChanged(); void imuRotationsChanged();
@ -119,6 +127,9 @@ namespace KWin
qreal m_focusedDisplayDistance = 0.85; qreal m_focusedDisplayDistance = 0.85;
qreal m_allDisplaysDistance = 1.05; qreal m_allDisplaysDistance = 1.05;
qreal m_displaySpacing = 0.0; 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<Output *> m_virtualOutputs; QList<Output *> m_virtualOutputs;
}; };

View File

@ -14,16 +14,13 @@
#include <KPluginFactory> #include <KPluginFactory>
#include <QAction> #include <QAction>
#include <QTableWidget> #include <QKeyEvent>
#include <QHeaderView>
#include <QPushButton>
#include <QLineEdit> #include <QLineEdit>
#include <QLabel> #include <QLabel>
#include <QJsonValue> #include <QJsonValue>
#include <QJsonArray> #include <QJsonArray>
#include <QPushButton>
#include <QFileDialog> #include <QComboBox>
#include <QKeyEvent>
Q_LOGGING_CATEGORY(KWIN_XR, "kwin.xr") 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_FocusedDisplayDistance, &QSlider::valueChanged, this, &BreezyDesktopEffectConfig::save);
connect(ui.kcfg_AllDisplaysDistance, &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_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<int>(&QComboBox::currentIndexChanged), this, &BreezyDesktopEffectConfig::save);
if (auto label = widget()->findChild<QLabel*>("labelAppNameVersion")) { if (auto label = widget()->findChild<QLabel*>("labelAppNameVersion")) {
label->setText(QStringLiteral("Breezy Desktop - v%1").arg(QLatin1String(BREEZY_DESKTOP_VERSION_STR))); 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_FocusedDisplayDistance->setValue(BreezyDesktopConfig::self()->focusedDisplayDistance());
ui.kcfg_AllDisplaysDistance->setValue(BreezyDesktopConfig::self()->allDisplaysDistance()); ui.kcfg_AllDisplaysDistance->setValue(BreezyDesktopConfig::self()->allDisplaysDistance());
ui.kcfg_DisplaySpacing->setValue(BreezyDesktopConfig::self()->displaySpacing()); 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_ZoomOnFocusEnabled->setChecked(BreezyDesktopConfig::self()->zoomOnFocusEnabled());
ui.kcfg_FocusedDisplayDistance->setEnabled(ui.kcfg_ZoomOnFocusEnabled->isChecked()); ui.kcfg_FocusedDisplayDistance->setEnabled(ui.kcfg_ZoomOnFocusEnabled->isChecked());
} }

View File

@ -2,6 +2,9 @@
<ui version="4.0"> <ui version="4.0">
<class>BreezyDesktopEffectConfig</class> <class>BreezyDesktopEffectConfig</class>
<widget class="QWidget" name="BreezyDesktopEffectConfig"> <widget class="QWidget" name="BreezyDesktopEffectConfig">
<property name="minimumWidth">
<number>800</number>
</property>
<layout class="QVBoxLayout" name="verticalLayout"> <layout class="QVBoxLayout" name="verticalLayout">
<item> <item>
<widget class="QLabel" name="labelDeviceConnectionStatus"> <widget class="QLabel" name="labelDeviceConnectionStatus">
@ -32,7 +35,7 @@
<attribute name="title"> <attribute name="title">
<string>&amp;General</string> <string>&amp;General</string>
</attribute> </attribute>
<layout class="QFormLayout" name="formLayout"> <layout class="QFormLayout" name="formLayout">
<item row="0" column="0"> <item row="0" column="0">
<widget class="QCheckBox" name="kcfg_ZoomOnFocusEnabled"> <widget class="QCheckBox" name="kcfg_ZoomOnFocusEnabled">
<property name="text"> <property name="text">
@ -112,7 +115,59 @@
</property> </property>
</widget> </widget>
</item> </item>
<item row="4" column="0" colspan="2"> <item row="4" column="0">
<widget class="QLabel" name="labelDisplayHorizontalOffset">
<property name="text">
<string>Display Horizontal Offset:</string>
</property>
</widget>
</item>
<item row="4" column="1">
<widget class="LabeledSlider" name="kcfg_DisplayHorizontalOffset">
<property name="decimalShift">
<double>2</double>
</property>
<property name="tickPosition">
<enum>QSlider::TicksBelow</enum>
</property>
<property name="tickInterval">
<double>50</double>
</property>
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="tracking">
<bool>true</bool>
</property>
</widget>
</item>
<item row="5" column="0">
<widget class="QLabel" name="labelDisplayVerticalOffset">
<property name="text">
<string>Display Vertical Offset:</string>
</property>
</widget>
</item>
<item row="5" column="1">
<widget class="LabeledSlider" name="kcfg_DisplayVerticalOffset">
<property name="decimalShift">
<double>2</double>
</property>
<property name="tickPosition">
<enum>QSlider::TicksBelow</enum>
</property>
<property name="tickInterval">
<double>50</double>
</property>
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="tracking">
<bool>true</bool>
</property>
</widget>
</item>
<item row="6" column="0" colspan="2">
<widget class="KShortcutsEditor" name="shortcutsEditor" native="true"> <widget class="KShortcutsEditor" name="shortcutsEditor" native="true">
<property name="sizePolicy"> <property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Preferred"> <sizepolicy hsizetype="Preferred" vsizetype="Preferred">
@ -128,17 +183,38 @@
<attribute name="title"> <attribute name="title">
<string>&amp;Advanced</string> <string>&amp;Advanced</string>
</attribute> </attribute>
<layout class="QVBoxLayout" name="verticalLayoutAdvanced"> <layout class="QFormLayout" name="formAdvanced">
<item> <item row="0" column="0">
<widget class="QLabel" name="labelAdvancedPlaceholder"> <widget class="QLabel" name="labelDisplayWrappingScheme">
<property name="text"> <property name="text">
<string>Advanced settings will appear here.</string> <string>Display Wrapping Scheme:</string>
</property> </property>
<property name="alignment"> </widget>
<set>Qt::AlignCenter</set> </item>
</property> <item row="0" column="1">
</widget> <widget class="QComboBox" name="kcfg_DisplayWrappingScheme">
</item> <item>
<property name="text">
<string>Auto</string>
</property>
</item>
<item>
<property name="text">
<string>Horizontal</string>
</property>
</item>
<item>
<property name="text">
<string>Vertical</string>
</property>
</item>
<item>
<property name="text">
<string>Flat</string>
</property>
</item>
</widget>
</item>
</layout> </layout>
</widget> </widget>
<widget class="QWidget" name="tabLicenseDetails"> <widget class="QWidget" name="tabLicenseDetails">

View File

@ -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 aspect = viewportWidth / viewportHeight;
const fovRadians = diagonalToCrossFOVs(degreeToRadian(viewportDiagonalFOV), aspect); const fovRadians = diagonalToCrossFOVs(degreeToRadian(viewportDiagonalFOV), aspect);
const defaultDistanceVerticalRadians = 2 * Math.atan(Math.tan(fovRadians.vertical / 2) / defaultDisplayDistanace); const defaultDistanceVerticalRadians = 2 * Math.atan(Math.tan(fovRadians.vertical / 2) / defaultDisplayDistance);
const defaultDistanceHorizontalRadians = 2 * Math.atan(Math.tan(fovRadians.horizontal / 2) / defaultDisplayDistanace); const defaultDistanceHorizontalRadians = 2 * Math.atan(Math.tan(fovRadians.horizontal / 2) / defaultDisplayDistance);
// distance needed for the FOV-sized monitor to fill up the screen // distance needed for the FOV-sized monitor to fill up the screen
const fullScreenDistance = viewportHeight / 2 / Math.tan(fovRadians.vertical / 2); const fullScreenDistance = viewportHeight / 2 / Math.tan(fovRadians.vertical / 2);
@ -62,6 +62,11 @@ QtObject {
const lensToScreenDistance = viewportHeight / 2 / Math.tan(defaultDistanceVerticalRadians / 2); const lensToScreenDistance = viewportHeight / 2 / Math.tan(defaultDistanceVerticalRadians / 2);
const completeScreenDistancePixels = lensToScreenDistance + lensDistancePixels; 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 { return {
widthPixels: viewportWidth, widthPixels: viewportWidth,
heightPixels: viewportHeight, heightPixels: viewportHeight,
@ -69,7 +74,7 @@ QtObject {
defaultDistanceHorizontalRadians, defaultDistanceHorizontalRadians,
lensDistancePixels, lensDistancePixels,
completeScreenDistancePixels, completeScreenDistancePixels,
monitorWrappingScheme: actualWrapScheme(screens, viewportWidth, viewportHeight), monitorWrappingScheme: monitorWrappingScheme,
curvedDisplay: false // or true curvedDisplay: false // or true
}; };
} }

View File

@ -61,14 +61,24 @@ Item {
id: displays 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: { property var monitorPlacements: {
const dx = effect.displayHorizontalOffset * viewportResolution[0];
const dy = effect.displayVerticalOffset * viewportResolution[1];
const adjustedGeometries = screens.map(screen => { const adjustedGeometries = screens.map(screen => {
const g = screen.geometry; const g = screen.geometry;
return { return {
x: g.x - screensXMid, x: g.x - screensXMid + dx,
y: g.y - screensYMid, y: g.y - screensYMid + dy,
width: g.width, width: g.width,
height: g.height height: g.height
}; };