diff --git a/docs/user/configuration.md b/docs/user/configuration.md
index 68fabf73e..155848547 100644
--- a/docs/user/configuration.md
+++ b/docs/user/configuration.md
@@ -77,7 +77,6 @@ This section contains general options it will begin with `[core]`
| computerName | string | Name used to identify the computer [default: machine's hostname] |
| useHooks | `true` or `false` | If Windows uses hooks or not [default: true] |
| language | 639 language | The language to display the GUI in [default: en] |
-| wlClipboard | `true` or `false` | When true the wl-clipboard backend will be enabled [default: false] |
| enableEnterCommand | `true` or `false` | Should the enter command be triggered when the screen is entered [defaut: false] |
| enterCommand | command | A command to run when the screen is entered. |
| enableExitCommand | `true` or `false` | Should the exit command be triggered when the screen is exited [defaut: false] |
diff --git a/src/lib/common/Settings.h b/src/lib/common/Settings.h
index 594585834..e735e05df 100644
--- a/src/lib/common/Settings.h
+++ b/src/lib/common/Settings.h
@@ -56,7 +56,6 @@ public:
inline static const auto Display = QStringLiteral("core/display");
inline static const auto UseHooks = QStringLiteral("core/useHooks");
inline static const auto Language = QStringLiteral("core/language");
- inline static const auto UseWlClipboard = QStringLiteral("core/wlClipboard");
inline static const auto EnableEnterCommand = QStringLiteral("core/enableEnterCommand");
inline static const auto ScreenEnterCommand = QStringLiteral("core/enterCommand");
inline static const auto EnableExitCommand = QStringLiteral("core/enableExitCommand");
@@ -226,7 +225,6 @@ private:
, Settings::Core::ComputerName
, Settings::Core::Display
, Settings::Core::UseHooks
- , Settings::Core::UseWlClipboard
, Settings::Core::Language
, Settings::Daemon::ConfigFile
, Settings::Daemon::Elevate
@@ -280,7 +278,6 @@ private:
, Settings::Gui::ShowVersionInTitle
, Settings::Gui::IgnoreMissingKeyboardLayouts
, Settings::Core::PreventSleep
- , Settings::Core::UseWlClipboard
, Settings::Core::EnableEnterCommand
, Settings::Core::EnableExitCommand
, Settings::Client::DynamicConnectionRetry
diff --git a/src/lib/gui/dialogs/SettingsDialog.cpp b/src/lib/gui/dialogs/SettingsDialog.cpp
index cb8260d06..abedd70d6 100644
--- a/src/lib/gui/dialogs/SettingsDialog.cpp
+++ b/src/lib/gui/dialogs/SettingsDialog.cpp
@@ -130,7 +130,6 @@ void SettingsDialog::initConnections() const
connect(ui->cbElevateDaemon, &QCheckBox::toggled, this, &SettingsDialog::setButtonBoxEnabledButtons);
connect(ui->cbAutoUpdate, &QCheckBox::toggled, this, &SettingsDialog::setButtonBoxEnabledButtons);
connect(ui->cbGuiDebug, &QCheckBox::toggled, this, &SettingsDialog::setButtonBoxEnabledButtons);
- connect(ui->cbUseWlClipboard, &QCheckBox::toggled, this, &SettingsDialog::setButtonBoxEnabledButtons);
connect(ui->cbShowVersion, &QCheckBox::toggled, this, &SettingsDialog::setButtonBoxEnabledButtons);
connect(ui->cbRequireClientCert, &QCheckBox::toggled, this, &SettingsDialog::setButtonBoxEnabledButtons);
connect(ui->groupLogToFile, &QGroupBox::toggled, this, &SettingsDialog::setButtonBoxEnabledButtons);
@@ -242,7 +241,6 @@ void SettingsDialog::accept()
Settings::setValue(Settings::Security::CheckPeers, ui->cbRequireClientCert->isChecked());
Settings::setValue(Settings::Core::Language, I18N::nativeTo639Name(ui->comboLanguage->currentText()));
Settings::setValue(Settings::Log::GuiDebug, ui->cbGuiDebug->isChecked());
- Settings::setValue(Settings::Core::UseWlClipboard, ui->cbUseWlClipboard->isChecked());
Settings::setValue(Settings::Gui::ShowVersionInTitle, ui->cbShowVersion->isChecked());
Settings::setValue(Settings::Core::EnableEnterCommand, ui->cbRunEnterCommand->isChecked());
Settings::setValue(Settings::Core::EnableExitCommand, ui->cbRunExitCommand->isChecked());
@@ -271,7 +269,6 @@ void SettingsDialog::loadFromConfig()
ui->cbElevateDaemon->setChecked(Settings::value(Settings::Daemon::Elevate).toBool());
ui->cbAutoUpdate->setChecked(Settings::value(Settings::Gui::AutoUpdateCheck).toBool());
ui->cbGuiDebug->setChecked(Settings::value(Settings::Log::GuiDebug).toBool());
- ui->cbUseWlClipboard->setChecked(Settings::value(Settings::Core::UseWlClipboard).toBool());
ui->cbShowVersion->setChecked(Settings::value(Settings::Gui::ShowVersionInTitle).toBool());
ui->cbRunEnterCommand->setChecked(Settings::value(Settings::Core::EnableEnterCommand).toBool());
ui->cbRunExitCommand->setChecked(Settings::value(Settings::Core::EnableExitCommand).toBool());
@@ -402,14 +399,6 @@ void SettingsDialog::updateControls()
ui->groupService->setVisible(false);
}
- // wl-clipboard support only works on wayland.
- // options should only be available when we are *not* running on wayland.
- if (deskflow::platform::isWayland()) {
- ui->cbUseWlClipboard->setEnabled(writable);
- } else if (ui->widgetWlClipboard->isVisibleTo(ui->tabAdvanced)) {
- ui->widgetWlClipboard->setVisible(false);
- }
-
ui->widgetLogFilename->setEnabled(writable && logToFile);
updateTlsControls();
@@ -443,7 +432,6 @@ bool SettingsDialog::isModified() const
(ui->cbElevateDaemon->isChecked() != Settings::value(Settings::Daemon::Elevate).toBool()) ||
(ui->cbAutoUpdate->isChecked() != Settings::value(Settings::Gui::AutoUpdateCheck).toBool()) ||
(ui->cbGuiDebug->isChecked() != Settings::value(Settings::Log::GuiDebug).toBool()) ||
- (ui->cbUseWlClipboard->isChecked() != Settings::value(Settings::Core::UseWlClipboard).toBool()) ||
(ui->cbShowVersion->isChecked() != Settings::value(Settings::Gui::ShowVersionInTitle).toBool()) ||
(ui->rbIconMono->isChecked() != Settings::value(Settings::Gui::SymbolicTrayIcon).toBool()) ||
(ui->groupService->isChecked() != (processMode == Settings::ProcessMode::Service)) ||
@@ -479,7 +467,6 @@ bool SettingsDialog::isDefault() const
(ui->cbElevateDaemon->isChecked() == Settings::defaultValue(Settings::Daemon::Elevate).toBool()) &&
(ui->cbAutoUpdate->isChecked() == Settings::defaultValue(Settings::Gui::AutoUpdateCheck).toBool()) &&
(ui->cbGuiDebug->isChecked() == Settings::defaultValue(Settings::Log::GuiDebug).toBool()) &&
- (ui->cbUseWlClipboard->isChecked() == Settings::defaultValue(Settings::Core::UseWlClipboard).toBool()) &&
(ui->cbShowVersion->isChecked() == Settings::defaultValue(Settings::Gui::ShowVersionInTitle).toBool()) &&
(ui->rbIconMono->isChecked() == Settings::defaultValue(Settings::Gui::SymbolicTrayIcon).toBool()) &&
(ui->groupService->isChecked() == (processMode == Settings::ProcessMode::Service)) &&
@@ -508,7 +495,6 @@ void SettingsDialog::resetToDefault()
ui->cbElevateDaemon->setChecked(Settings::defaultValue(Settings::Daemon::Elevate).toBool());
ui->cbAutoUpdate->setChecked(Settings::defaultValue(Settings::Gui::AutoUpdateCheck).toBool());
ui->cbGuiDebug->setChecked(Settings::defaultValue(Settings::Log::GuiDebug).toBool());
- ui->cbUseWlClipboard->setChecked(Settings::defaultValue(Settings::Core::UseWlClipboard).toBool());
ui->cbShowVersion->setChecked(Settings::defaultValue(Settings::Gui::ShowVersionInTitle).toBool());
ui->cbRunEnterCommand->setChecked(Settings::defaultValue(Settings::Core::EnableEnterCommand).toBool());
ui->cbRunExitCommand->setChecked(Settings::defaultValue(Settings::Core::EnableExitCommand).toBool());
diff --git a/src/lib/gui/dialogs/SettingsDialog.ui b/src/lib/gui/dialogs/SettingsDialog.ui
index ae33488fc..95cad931b 100644
--- a/src/lib/gui/dialogs/SettingsDialog.ui
+++ b/src/lib/gui/dialogs/SettingsDialog.ui
@@ -807,29 +807,6 @@
- -
-
-
-
-
-
-
- Enable wl-clipboard support
-
-
-
- -
-
-
- <html><head/><body><p>Requires the wl-clipboard package</p><p>When using wl-clipboard v2.2.1, there is a focus stealing bug that may make Deskflow harder to use. This has been fixed when using the wl-clipboard master branch, unless your Compositor lacks wlroots-data-control protocol support.</p></body></html>
-
-
- true
-
-
-
-
-
-
-
diff --git a/src/lib/platform/CMakeLists.txt b/src/lib/platform/CMakeLists.txt
index a71be51e7..e8b686978 100644
--- a/src/lib/platform/CMakeLists.txt
+++ b/src/lib/platform/CMakeLists.txt
@@ -151,10 +151,6 @@ elseif(UNIX)
EiKeyState.h
EiScreen.cpp
EiScreen.h
- WlClipboardCollection.cpp
- WlClipboardCollection.h
- WlClipboard.cpp
- WlClipboard.h
)
# The Portal sources also require EI.
if(LIBPORTAL_FOUND)
diff --git a/src/lib/platform/WlClipboard.cpp b/src/lib/platform/WlClipboard.cpp
deleted file mode 100644
index 2277e1c52..000000000
--- a/src/lib/platform/WlClipboard.cpp
+++ /dev/null
@@ -1,411 +0,0 @@
-/*
- * Deskflow -- mouse and keyboard sharing utility
- * SPDX-FileCopyrightText: (C) 2025 Deskflow Developers
- * SPDX-License-Identifier: GPL-2.0-only WITH LicenseRef-OpenSSL-Exception
- */
-
-#include "platform/WlClipboard.h"
-
-#include "base/Log.h"
-
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-
-#include
-#include
-#include
-
-namespace {
-
-inline static const auto s_copyApp = QStringLiteral("wl-copy");
-inline static const auto s_pasteApp = QStringLiteral("wl-paste");
-
-// wl-clipboard args
-inline static const auto s_listTypes = QStringLiteral("--list-types");
-inline static const auto s_isPrimary = QStringLiteral("--primary");
-inline static const auto s_noNewLine = QStringLiteral("-n");
-inline static const auto s_readType = QStringLiteral("-t%1");
-
-// MIME types for different clipboard formats
-inline static const auto s_mimeTypeText = QStringLiteral("text/plain;charset=utf-8");
-inline static const auto s_mimeTypeHtml = QStringLiteral("text/html");
-inline static const auto s_mimeTypeBmp = QStringLiteral("image/bmp");
-
-// Additional HTML MIME type variants
-const char *const s_mimeTypeHtmlUtf8 = "text/html;charset=UTF-8";
-const char *const s_mimeTypeHtmlWindows = "HTML Format";
-
-// Command timeout (milliseconds)
-const int kCacheValidityMs = 100;
-const int kMonitorIntervalMs = 1000;
-const int kMaxConsecutiveErrors = 5;
-} // namespace
-
-WlClipboard::WlClipboard(ClipboardID id) : m_id(id), m_useClipboard(id == kClipboardClipboard)
-{
- // Initialize cached data
- for (int i = 0; i < static_cast(Format::TotalFormats); ++i) {
- m_cachedAvailable[i] = false;
- }
-}
-
-WlClipboard::~WlClipboard()
-{
- stopMonitoring();
- for (auto &cmd : m_runningWlCopies) {
- cmd->close();
- cmd->waitForFinished(100);
- }
- m_runningWlCopies.clear();
-}
-
-ClipboardID WlClipboard::getID() const
-{
- return m_id;
-}
-
-bool WlClipboard::isAvailable()
-{
- return !QStandardPaths::findExecutable(s_copyApp).isEmpty() && !QStandardPaths::findExecutable(s_pasteApp).isEmpty();
-}
-
-bool WlClipboard::isEnabled()
-{
- return Settings::value(Settings::Core::UseWlClipboard).toBool();
-}
-
-void WlClipboard::startMonitoring()
-{
- if (m_monitoring) {
- return;
- }
- m_stopMonitoring = false;
- m_monitoring = true;
- m_monitorThread = std::make_unique(&WlClipboard::monitorClipboard, this);
-}
-
-void WlClipboard::stopMonitoring()
-{
- if (!m_monitoring) {
- return;
- }
-
- m_stopMonitoring = true;
- m_monitoring = false;
-
- if (m_monitorThread && m_monitorThread->joinable()) {
- m_monitorThread->join();
- }
- m_monitorThread.reset();
-}
-
-bool WlClipboard::hasChanged() const
-{
- return m_hasChanged.load();
-}
-
-bool WlClipboard::empty()
-{
- if (!m_open) {
- return false;
- }
- auto cmd = new QProcess(this);
- cmd->setProgram(s_copyApp);
- m_runningWlCopies.append(cmd);
- connect(cmd, &QProcess::finished, this, [&] { m_runningWlCopies.removeAll(cmd); });
-
- QStringList args = {s_noNewLine, ""};
- if (!m_useClipboard)
- args.prepend(s_isPrimary);
-
- cmd->setArguments(args);
- cmd->start();
- bool success = cmd->waitForStarted(100);
-
- if (success) {
- // Update ownership and cache only if command succeeded
- std::scoped_lock lock(m_cacheMutex);
- updateOwnership(true);
- invalidateCache();
- }
-
- return success;
-}
-
-void WlClipboard::add(Format format, const std::string &data)
-{
- if (!m_open) {
- return;
- }
-
- if (format == Format::HTML) {
- return;
- }
-
- auto mimeType = formatToMimeType(format);
- if (mimeType.isEmpty()) {
- LOG_WARN("unsupported clipboard format: %d", format);
- return;
- }
-
- auto cmd = new QProcess(this);
- cmd->setProgram(s_copyApp);
-
- m_runningWlCopies.append(cmd);
- connect(cmd, &QProcess::finished, this, [&] { m_runningWlCopies.removeAll(cmd); });
-
- QStringList args = {s_noNewLine, s_readType.arg(mimeType), QString::fromStdString(data)};
- if (!m_useClipboard)
- args.prepend(s_isPrimary);
-
- cmd->setArguments(args);
- cmd->start();
-
- if (cmd->waitForStarted(100)) {
- std::scoped_lock lock(m_cacheMutex);
- updateOwnership(true);
- invalidateCache();
- }
-}
-
-bool WlClipboard::open(Time time) const
-{
- if (m_open) {
- LOG_DEBUG("failed to open clipboard: already opened");
- return false;
- }
-
- m_open = true;
- m_time = time;
-
- return true;
-}
-
-void WlClipboard::close() const
-{
- if (!m_open) {
- return;
- }
-
- LOG_DEBUG("close clipboard");
-
- m_open = false;
- const_cast(this)->invalidateCache();
-}
-
-IClipboard::Time WlClipboard::getTime() const
-{
- return m_time;
-}
-
-bool WlClipboard::has(Format format) const
-{
- if (!m_open) {
- return false;
- }
-
- std::scoped_lock lock(m_cacheMutex);
-
- // Check cache validity
- Time currentTime = getCurrentTime();
- if (m_cached && (currentTime - m_cachedTime) < kCacheValidityMs) {
- return m_cachedAvailable[static_cast(format)];
- }
-
- if (const auto availableTypes = getAvailableMimeTypes(); availableTypes.isEmpty()) {
- // No types available - mark all formats as unavailable
- for (int i = 0; i < static_cast(Format::TotalFormats); ++i) {
- m_cachedAvailable[i] = false;
- m_cachedData[i].clear();
- }
- } else {
- using enum IClipboard::Format;
- // Check each format against available types
- for (int i = 0; i < static_cast(TotalFormats); ++i) {
- auto currentFormat = static_cast(i);
- const auto mimeType = formatToMimeType(currentFormat);
-
- m_cachedAvailable[i] = false;
- if (!mimeType.isEmpty()) {
- for (const auto &available : availableTypes) {
- if (available == mimeType || (currentFormat == Text && available == QStringLiteral("text/plain")) ||
- (currentFormat == HTML && available.startsWith(QStringLiteral("text/html")))) {
- m_cachedAvailable[i] = true;
- break;
- }
- }
- }
- }
- }
-
- m_cached = true;
- m_cachedTime = currentTime;
-
- return m_cachedAvailable[static_cast(format)];
-}
-
-std::string WlClipboard::get(Format format) const
-{
- if (!m_open) {
- return std::string();
- }
-
- std::scoped_lock lock(m_cacheMutex);
-
- // Return cached data if available and valid
- if (m_cached && m_cachedAvailable[static_cast(format)] && !m_cachedData[static_cast(format)].empty()) {
- return m_cachedData[static_cast(format)];
- }
-
- auto mimeType = formatToMimeType(format);
- if (mimeType.isEmpty()) {
- return std::string();
- }
-
- QProcess cmd;
- cmd.setProgram(s_pasteApp);
-
- QStringList args = {s_noNewLine, s_readType.arg(mimeType)};
- if (!m_useClipboard)
- args.append(s_isPrimary);
-
- cmd.setArguments(args);
- cmd.start();
- cmd.waitForFinished();
-
- auto data = cmd.readAll().toStdString();
-
- // Update cache
- m_cachedData[static_cast(format)] = data;
- m_cachedAvailable[static_cast(format)] = !data.empty();
- m_cached = true;
- m_cachedTime = getCurrentTime();
-
- return data;
-}
-
-QString WlClipboard::formatToMimeType(Format format) const
-{
- switch (format) {
- using enum IClipboard::Format;
- case Text:
- return s_mimeTypeText;
- case HTML:
- return s_mimeTypeHtml;
- case Bitmap:
- return s_mimeTypeBmp;
- default:
- return {};
- }
-}
-
-IClipboard::Format WlClipboard::mimeTypeToFormat(const QString &mimeType) const
-{
- using enum IClipboard::Format;
- if (mimeType == s_mimeTypeText || mimeType == QStringLiteral("text/plain")) {
- return Text;
- }
- if (mimeType == s_mimeTypeHtml || mimeType == s_mimeTypeHtmlUtf8 || mimeType == s_mimeTypeHtmlWindows ||
- mimeType.contains("text/html")) {
- return HTML;
- }
- if (mimeType == s_mimeTypeBmp) {
- return Bitmap;
- }
- return Text; // Default fallback
-}
-
-QStringList WlClipboard::getAvailableMimeTypes() const
-{
- QProcess cmd;
- cmd.setProgram(s_pasteApp);
-
- QStringList args = {s_listTypes};
- if (!m_useClipboard)
- args.append(s_isPrimary);
-
- cmd.setArguments(args);
- cmd.start();
- cmd.waitForFinished();
-
- const static QChar newLine = QLatin1Char('\n');
- return QString::fromLocal8Bit(cmd.readAll()).split(newLine);
-}
-
-void WlClipboard::monitorClipboard()
-{
- QStringList lastTypes;
- int consecutiveErrors = 0;
-
- while (!m_stopMonitoring) {
- std::this_thread::sleep_for(std::chrono::milliseconds(kMonitorIntervalMs));
- try {
- // Check if clipboard content has changed by comparing available types
- const auto currentTypes = getAvailableMimeTypes();
-
- // Reset error counter on successful operation
- consecutiveErrors = 0;
-
- if (currentTypes != lastTypes) {
- m_hasChanged = true;
- lastTypes = currentTypes;
-
- // Clear cache when clipboard changes
- std::scoped_lock lock(m_cacheMutex);
- invalidateCache();
- updateOwnership(false);
- }
- } catch (const std::exception &e) {
- LOG_WARN("clipboard monitoring error: %s", e.what());
- if (++consecutiveErrors >= kMaxConsecutiveErrors) {
- LOG_ERR("too many consecutive errors in clipboard monitoring, stopping");
- break;
- }
- } catch (...) {
- LOG_WARN("clipboard monitoring unknown error");
- if (++consecutiveErrors >= kMaxConsecutiveErrors) {
- LOG_ERR("too many consecutive errors in clipboard monitoring, stopping");
- break;
- }
- }
- }
-}
-
-IClipboard::Time WlClipboard::getCurrentTime() const
-{
- return static_cast