diff --git a/kwin/src/breezydesktopconfig.kcfg b/kwin/src/breezydesktopconfig.kcfg
index d65e0ea..33db6a1 100644
--- a/kwin/src/breezydesktopconfig.kcfg
+++ b/kwin/src/breezydesktopconfig.kcfg
@@ -67,6 +67,13 @@
Whether to remove any virtual displays when the effect is disabled
+
+ 15
+ 1
+ 45
+
+ How closely the display follows
+
-1
-1
diff --git a/kwin/src/breezydesktopeffect.cpp b/kwin/src/breezydesktopeffect.cpp
index 5520e32..5d0f028 100644
--- a/kwin/src/breezydesktopeffect.cpp
+++ b/kwin/src/breezydesktopeffect.cpp
@@ -118,6 +118,10 @@ BreezyDesktopEffect::BreezyDesktopEffect()
this->setZoomOnFocusEnabled(!m_zoomOnFocusEnabled);
}
);
+ setupGlobalShortcut(
+ BreezyShortcuts::TOGGLE_FOLLOW_MODE,
+ [this]() { this->toggleSmoothFollow(); }
+ );
connect(effects, &EffectsHandler::cursorShapeChanged, this, &BreezyDesktopEffect::updateCursorImage);
updateCursorImage();
@@ -194,9 +198,9 @@ void BreezyDesktopEffect::setupGlobalShortcut(const BreezyShortcuts::Shortcut &s
}
void BreezyDesktopEffect::recenter() {
- XRDriverIPC::instance().writeControlFlags({
- {"recenter_screen", true}
- });
+ QJsonObject flags;
+ flags.insert(QStringLiteral("recenter_screen"), true);
+ XRDriverIPC::instance().writeControlFlags(flags);
}
void BreezyDesktopEffect::reconfigure(ReconfigureFlags)
@@ -378,6 +382,12 @@ void BreezyDesktopEffect::setZoomOnFocusEnabled(bool enabled) {
}
}
+void BreezyDesktopEffect::toggleSmoothFollow() {
+ QJsonObject flags;
+ flags.insert(QStringLiteral("toggle_breezy_desktop_smooth_follow"), true);
+ XRDriverIPC::instance().writeControlFlags(flags);
+}
+
bool BreezyDesktopEffect::imuResetState() const {
return m_imuResetState;
}
@@ -421,6 +431,8 @@ void BreezyDesktopEffect::setFocusedDisplayDistance(qreal distance) {
if (distance != m_focusedDisplayDistance) {
m_focusedDisplayDistance = std::clamp(distance, 0.2, m_allDisplaysDistance);
Q_EMIT focusedDisplayDistanceChanged();
+
+ if (m_smoothFollowEnabled) updateDriverDisplayDistance(m_focusedDisplayDistance);
}
}
@@ -661,7 +673,15 @@ void BreezyDesktopEffect::updateImuRotation() {
if (m_smoothFollowEnabled != nextSmoothFollowEnabled) {
m_smoothFollowEnabled = nextSmoothFollowEnabled;
Q_EMIT smoothFollowEnabledChanged();
- }
+
+ if (nextSmoothFollowEnabled) updateDriverDisplayDistance(m_focusedDisplayDistance);
+ } else if (enabled && !wasEnabled) Q_EMIT smoothFollowEnabledChanged();
+}
+
+void BreezyDesktopEffect::updateDriverDisplayDistance(float distance) {
+ QJsonObject flags;
+ flags.insert(QStringLiteral("breezy_desktop_display_distance"), distance);
+ XRDriverIPC::instance().writeControlFlags(flags);
}
QString BreezyDesktopEffect::cursorImageSource() const
diff --git a/kwin/src/breezydesktopeffect.h b/kwin/src/breezydesktopeffect.h
index f7f1c2a..4c15f89 100644
--- a/kwin/src/breezydesktopeffect.h
+++ b/kwin/src/breezydesktopeffect.h
@@ -132,6 +132,8 @@ namespace KWin
void setupGlobalShortcut(const BreezyShortcuts::Shortcut &shortcut,
std::function triggeredFunc);
void recenter();
+ void toggleSmoothFollow();
+ void updateDriverDisplayDistance(float distance);
QString m_cursorImageSource;
QSize m_cursorImageSize;
diff --git a/kwin/src/kcm/breezydesktopeffectkcm.cpp b/kwin/src/kcm/breezydesktopeffectkcm.cpp
index 2e4c2fb..5373f8c 100644
--- a/kwin/src/kcm/breezydesktopeffectkcm.cpp
+++ b/kwin/src/kcm/breezydesktopeffectkcm.cpp
@@ -107,13 +107,16 @@ BreezyDesktopEffectConfig::BreezyDesktopEffectConfig(QObject *parent, const KPlu
addShortcutAction(actionCollection, BreezyShortcuts::TOGGLE);
addShortcutAction(actionCollection, BreezyShortcuts::RECENTER);
addShortcutAction(actionCollection, BreezyShortcuts::TOGGLE_ZOOM_ON_FOCUS);
+ addShortcutAction(actionCollection, BreezyShortcuts::TOGGLE_FOLLOW_MODE);
ui.shortcutsEditor->addCollection(actionCollection);
connect(ui.shortcutsEditor, &KShortcutsEditor::keyChange, this, &BreezyDesktopEffectConfig::markAsChanged);
connect(ui.EffectEnabled, &QCheckBox::toggled, this, &BreezyDesktopEffectConfig::updateDriverEnabled);
+ connect(ui.SmoothFollowEnabled, &QCheckBox::toggled, this, &BreezyDesktopEffectConfig::updateSmoothFollowEnabled);
connect(ui.kcfg_ZoomOnFocusEnabled, &QCheckBox::toggled, this, &BreezyDesktopEffectConfig::save);
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_SmoothFollowThreshold, &QSlider::valueChanged, this, &BreezyDesktopEffectConfig::updateSmoothFollowThreshold);
connect(ui.kcfg_DisplayHorizontalOffset, &QSlider::valueChanged, this, &BreezyDesktopEffectConfig::save);
connect(ui.kcfg_DisplayVerticalOffset, &QSlider::valueChanged, this, &BreezyDesktopEffectConfig::save);
connect(ui.kcfg_LookAheadOverride, &QSlider::valueChanged, this, &BreezyDesktopEffectConfig::save);
@@ -151,7 +154,9 @@ BreezyDesktopEffectConfig::BreezyDesktopEffectConfig(QObject *parent, const KPlu
labelStatus->setVisible(false);
bool success = XRDriverIPC::instance().verifyToken(edit->text().trimmed().toStdString());
if (success) {
- XRDriverIPC::instance().writeControlFlags({{"refresh_device_license", true}});
+ QJsonObject flags;
+ flags.insert(QStringLiteral("refresh_device_license"), true);
+ XRDriverIPC::instance().writeControlFlags(flags);
}
showStatus(labelStatus, success, success ? tr("Your license has been refreshed.") : tr("Invalid or expired token."));
setRequestInProgress({edit, sender()}, false);
@@ -216,7 +221,8 @@ void BreezyDesktopEffectConfig::save()
updateConfigFromUi();
BreezyDesktopConfig::self()->save();
KCModule::save();
- ui.kcfg_FocusedDisplayDistance->setEnabled(ui.kcfg_ZoomOnFocusEnabled->isChecked());
+ ui.kcfg_FocusedDisplayDistance->setEnabled(
+ ui.kcfg_ZoomOnFocusEnabled->isChecked() || ui.SmoothFollowEnabled->isChecked());
m_updatingFromConfig = false;
updateUnmanagedState();
@@ -250,6 +256,7 @@ void BreezyDesktopEffectConfig::updateUiFromConfig()
ui.kcfg_RemoveVirtualDisplaysOnDisable->setChecked(BreezyDesktopConfig::self()->removeVirtualDisplaysOnDisable());
ui.kcfg_ZoomOnFocusEnabled->setChecked(BreezyDesktopConfig::self()->zoomOnFocusEnabled());
ui.kcfg_FocusedDisplayDistance->setEnabled(ui.kcfg_ZoomOnFocusEnabled->isChecked());
+ ui.kcfg_SmoothFollowThreshold->setValue(BreezyDesktopConfig::self()->smoothFollowThreshold());
}
void BreezyDesktopEffectConfig::updateUiFromDefaultConfig()
@@ -446,6 +453,13 @@ void BreezyDesktopEffectConfig::pollDriverState()
m_connectedDeviceBrand = stateJson.value(QStringLiteral("connected_device_brand")).toString();
m_connectedDeviceModel = stateJson.value(QStringLiteral("connected_device_model")).toString();
+ const bool smoothFollow = smoothFollowEnabled(stateJsonOpt);
+ if (ui.SmoothFollowEnabled->isChecked() != smoothFollow) {
+ ui.SmoothFollowEnabled->setChecked(smoothFollow);
+
+ ui.kcfg_FocusedDisplayDistance->setEnabled(ui.kcfg_ZoomOnFocusEnabled->isChecked() || smoothFollow);
+ }
+
const bool wasDeviceConnected = m_deviceConnected;
m_deviceConnected = !m_connectedDeviceBrand.isEmpty() && !m_connectedDeviceModel.isEmpty();
if (!m_driverStateInitialized || m_deviceConnected != wasDeviceConnected) {
@@ -471,6 +485,13 @@ bool BreezyDesktopEffectConfig::multitapEnabled(std::optional confi
return configJson.value(QStringLiteral("multi_tap_enabled")).toBool();
}
+bool BreezyDesktopEffectConfig::smoothFollowEnabled(std::optional stateJsonOpt)
+{
+ if (!stateJsonOpt) return false;
+ auto stateJson = stateJsonOpt.value();
+ return stateJson.value(QStringLiteral("breezy_desktop_smooth_follow_enabled")).toBool();
+}
+
void BreezyDesktopEffectConfig::updateMultitapEnabled()
{
auto configJsonOpt = XRDriverIPC::instance().retrieveConfig();
@@ -486,6 +507,26 @@ void BreezyDesktopEffectConfig::updateMultitapEnabled()
XRDriverIPC::instance().writeConfig(newConfig);
}
+void BreezyDesktopEffectConfig::updateSmoothFollowEnabled()
+{
+ auto stateJsonOpt = XRDriverIPC::instance().retrieveDriverState();
+ if (smoothFollowEnabled(stateJsonOpt) == ui.SmoothFollowEnabled->isChecked()) {
+ return;
+ }
+ bool enabled = ui.SmoothFollowEnabled->isChecked();
+ QJsonObject flags;
+ flags.insert(QStringLiteral("enable_breezy_desktop_smooth_follow"), enabled);
+ XRDriverIPC::instance().writeControlFlags(flags);
+}
+
+void BreezyDesktopEffectConfig::updateSmoothFollowThreshold()
+{
+ BreezyDesktopEffectConfig::save();
+ QJsonObject flags;
+ flags.insert(QStringLiteral("breezy_desktop_follow_threshold"), ui.kcfg_SmoothFollowThreshold->value());
+ XRDriverIPC::instance().writeControlFlags(flags);
+}
+
void BreezyDesktopEffectConfig::showStatus(QLabel *label, bool success, const QString &message) {
if (!label) return;
QPalette pal = label->palette();
diff --git a/kwin/src/kcm/breezydesktopeffectkcm.h b/kwin/src/kcm/breezydesktopeffectkcm.h
index 8ec8404..4a8451e 100644
--- a/kwin/src/kcm/breezydesktopeffectkcm.h
+++ b/kwin/src/kcm/breezydesktopeffectkcm.h
@@ -30,12 +30,15 @@ public Q_SLOTS:
private:
void updateDriverEnabled();
void updateMultitapEnabled();
+ void updateSmoothFollowEnabled();
+ void updateSmoothFollowThreshold();
void updateUiFromConfig();
void updateUiFromDefaultConfig();
void updateConfigFromUi();
void updateUnmanagedState();
bool driverEnabled(std::optional configJsonOpt);
bool multitapEnabled(std::optional configJsonOpt);
+ bool smoothFollowEnabled(std::optional stateJsonOpt);
void pollDriverState();
void refreshLicenseUi(const QJsonObject &rootObj);
void checkEffectLoaded();
@@ -55,6 +58,8 @@ private:
bool m_updatingFromConfig = false;
bool m_driverStateInitialized = false;
bool m_deviceConnected = false;
+ bool m_smoothFollowEnabled = false;
+ int m_smoothFollowThreshold = 15;
QString m_connectedDeviceBrand;
QString m_connectedDeviceModel;
QTimer m_statePollTimer; // periodic driver state polling
diff --git a/kwin/src/kcm/breezydesktopeffectkcm.ui b/kwin/src/kcm/breezydesktopeffectkcm.ui
index 4061954..f3d42bc 100644
--- a/kwin/src/kcm/breezydesktopeffectkcm.ui
+++ b/kwin/src/kcm/breezydesktopeffectkcm.ui
@@ -72,14 +72,24 @@
- -
+
-
+
+
+ Follow mode
+
+
+ false
+
+
+
+ -
Focused Display Distance:
- -
+
-
2
@@ -101,14 +111,14 @@
- -
+
-
All Displays Distance:
- -
+
-
2
@@ -130,14 +140,14 @@
- -
+
-
Display Spacing:
- -
+
-
Qt::Horizontal
@@ -147,7 +157,36 @@
- -
+
-
+
+
+ Follow threshold:
+
+
+
+ -
+
+
+ Qt::Horizontal
+
+
+ false
+
+
+ QSlider::NoTicks
+
+
+ 9
+
+
+ 10
+
+
+ Qt::Horizontal
+
+
+
+ -
Add Virtual Display:
@@ -160,7 +199,7 @@
- -
+
-
false
@@ -193,7 +232,7 @@
- -
+
-
false
false
@@ -206,7 +245,7 @@
- -
+
-
diff --git a/kwin/src/kcm/shortcuts.h b/kwin/src/kcm/shortcuts.h
index f5792a6..1fd778d 100644
--- a/kwin/src/kcm/shortcuts.h
+++ b/kwin/src/kcm/shortcuts.h
@@ -28,4 +28,10 @@ namespace BreezyShortcuts {
QStringLiteral("Toggle Zoom on Focus"),
QStringLiteral("Toggle Zoom on Focus")
};
+
+ const Shortcut TOGGLE_FOLLOW_MODE = {
+ Qt::CTRL | Qt::META | Qt::Key_Return,
+ QStringLiteral("Toggle Follow Mode"),
+ QStringLiteral("Toggle Follow Mode")
+ };
}
diff --git a/kwin/src/xrdriveripc/xrdriveripc.cpp b/kwin/src/xrdriveripc/xrdriveripc.cpp
index 4508f3d..2e93e56 100644
--- a/kwin/src/xrdriveripc/xrdriveripc.cpp
+++ b/kwin/src/xrdriveripc/xrdriveripc.cpp
@@ -89,9 +89,8 @@ bool XRDriverIPC::writeConfig(const QJsonObject &configUpdate) {
return !out.isEmpty();
}
-bool XRDriverIPC::writeControlFlags(const std::map &flags) {
- QJsonObject obj; for (const auto &kv : flags) obj.insert(QString::fromStdString(kv.first), kv.second);
- QByteArray payload = QJsonDocument(obj).toJson(QJsonDocument::Compact);
+bool XRDriverIPC::writeControlFlags(const QJsonObject &flags) {
+ QByteArray payload = QJsonDocument(flags).toJson(QJsonDocument::Compact);
QByteArray out = invokePython(QStringLiteral("write_control_flags"), payload, {});
return !out.isEmpty();
}
diff --git a/kwin/src/xrdriveripc/xrdriveripc.h b/kwin/src/xrdriveripc/xrdriveripc.h
index ee6ca81..d349c6e 100644
--- a/kwin/src/xrdriveripc/xrdriveripc.h
+++ b/kwin/src/xrdriveripc/xrdriveripc.h
@@ -82,7 +82,7 @@ public:
std::optional retrieveConfig();
std::optional retrieveDriverState();
bool writeConfig(const QJsonObject &configUpdate);
- bool writeControlFlags(const std::map &flags);
+ bool writeControlFlags(const QJsonObject &flags);
bool requestToken(const std::string &email);
bool verifyToken(const std::string &token);
diff --git a/ui/modules/PyXRLinuxDriverIPC b/ui/modules/PyXRLinuxDriverIPC
index 7fecfc6..d047bdf 160000
--- a/ui/modules/PyXRLinuxDriverIPC
+++ b/ui/modules/PyXRLinuxDriverIPC
@@ -1 +1 @@
-Subproject commit 7fecfc604b553b9155837c58aaae423ba00afc63
+Subproject commit d047bdf7b7300751a09464b6f62d67cb3e291031