Add warnings for missing Wayland libs and features (#7492)
* Add warnings for libei and libportal * Add warning about experimental Wayland support * Update ChangeLog * Move GUI res files to root res dir * Fixed crazy qrc shenanigans * Move Wayland warning logic, add tests, improve core retry * Also install py modules with pip since meson can be in venv * Throw error when no libei * Add warnings when no libportal * Allow failure then success to show related to Wayland (for switching modes) * Fixed formatting * Fixed includes * Simplify warning message * Add log hint * Show warning when error already shown * Only check for Wayland if X or libei * Add error message when quitting dur to remote desktop error * Improve consistency of portal log lines
@ -19,6 +19,7 @@ Enhancements:
|
||||
- #7485 Use `.venv` dir for as Python venv and cache
|
||||
- #7488 Only wait for elevated process to end when arg is set
|
||||
- #7489 Add `--config-toml` arg for TOML config file
|
||||
- #7492 Add warnings for `libei` and `libportal`
|
||||
|
||||
# 1.15.1
|
||||
|
||||
|
||||
@ -161,7 +161,8 @@ config:
|
||||
sudo apt-get install -y \
|
||||
python3-attr \
|
||||
python3-jinja2 \
|
||||
libsystemd-dev
|
||||
libsystemd-dev &&
|
||||
pip install attrs jinja2
|
||||
|
||||
ubuntu: *debian_libei
|
||||
linuxmint: *debian_libei
|
||||
@ -170,7 +171,8 @@ config:
|
||||
sudo dnf install -y \
|
||||
python3-attrs \
|
||||
python3-jinja2 \
|
||||
systemd-devel
|
||||
systemd-devel &&
|
||||
pip install attrs jinja2
|
||||
|
||||
rhel: *fedora_libei
|
||||
rocky: *fedora_libei
|
||||
|
||||
@ -25,6 +25,7 @@
|
||||
"hotspots",
|
||||
"Hutterer",
|
||||
"ifdef",
|
||||
"INPUTCAPTURE",
|
||||
"integtests",
|
||||
"keychain",
|
||||
"Keychains",
|
||||
@ -69,7 +70,8 @@
|
||||
"vmactions",
|
||||
"Volker",
|
||||
"whot",
|
||||
"winget"
|
||||
"winget",
|
||||
"XWINDOWS"
|
||||
],
|
||||
"ignoreWords": [],
|
||||
"import": []
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
<RCC>
|
||||
<qresource prefix="/res">
|
||||
<qresource>
|
||||
<file>icons/16x16/synergy.png</file>
|
||||
<file>icons/64x64/video-display.png</file>
|
||||
<file>icons/64x64/user-trash.png</file>
|
||||
|
Before Width: | Height: | Size: 450 B After Width: | Height: | Size: 450 B |
|
Before Width: | Height: | Size: 905 B After Width: | Height: | Size: 905 B |
|
Before Width: | Height: | Size: 693 B After Width: | Height: | Size: 693 B |
|
Before Width: | Height: | Size: 99 KiB After Width: | Height: | Size: 99 KiB |
|
Before Width: | Height: | Size: 919 B After Width: | Height: | Size: 919 B |
|
Before Width: | Height: | Size: 1.0 KiB After Width: | Height: | Size: 1.0 KiB |
|
Before Width: | Height: | Size: 1.7 KiB After Width: | Height: | Size: 1.7 KiB |
|
Before Width: | Height: | Size: 3.7 KiB After Width: | Height: | Size: 3.7 KiB |
|
Before Width: | Height: | Size: 2.5 KiB After Width: | Height: | Size: 2.5 KiB |
|
Before Width: | Height: | Size: 6.1 KiB After Width: | Height: | Size: 6.1 KiB |
|
Before Width: | Height: | Size: 7.4 KiB After Width: | Height: | Size: 7.4 KiB |
|
Before Width: | Height: | Size: 12 KiB After Width: | Height: | Size: 12 KiB |
|
Before Width: | Height: | Size: 11 KiB After Width: | Height: | Size: 11 KiB |
@ -20,26 +20,23 @@ set(CMAKE_AUTORCC ON)
|
||||
set(CMAKE_AUTOUIC ON)
|
||||
set(CMAKE_INCLUDE_CURRENT_DIR ON)
|
||||
|
||||
set(res_dir ${CMAKE_SOURCE_DIR}/res/gui)
|
||||
set(qrc_file ${res_dir}/Synergy.qrc)
|
||||
|
||||
file(
|
||||
GLOB
|
||||
GUI_SOURCE_FILES
|
||||
sources
|
||||
src/*.cpp
|
||||
src/*.h
|
||||
src/validators/*
|
||||
src/widgets/*)
|
||||
file(GLOB GUI_UI_FILES src/*.ui)
|
||||
file(GLOB ui_files src/*.ui)
|
||||
|
||||
if(WIN32)
|
||||
set(GUI_RC_FILES res/win/Synergy.rc ${CMAKE_BINARY_DIR}/src/version.rc)
|
||||
set(rc_files ${res_dir}/win/Synergy.rc ${CMAKE_BINARY_DIR}/src/version.rc)
|
||||
endif()
|
||||
|
||||
add_executable(
|
||||
${target} WIN32
|
||||
${GUI_SOURCE_FILES}
|
||||
${GUI_UI_FILES}
|
||||
${GUI_RC_FILES}
|
||||
res/Synergy.qrc
|
||||
${QM_FILES})
|
||||
add_executable(${target} WIN32 ${sources} ${ui_files} ${rc_files} ${qrc_file})
|
||||
|
||||
# regular exe headers
|
||||
include_directories(./src)
|
||||
|
||||
@ -55,7 +55,7 @@ int AboutDialog::exec() {
|
||||
void AboutDialog::updateLogo() const {
|
||||
if (isDarkMode()) {
|
||||
qDebug("dark mode detected, showing dark logo");
|
||||
QPixmap logo(":/res/image/about-dark.png");
|
||||
QPixmap logo(":/image/about-dark.png");
|
||||
if (!logo.isNull()) {
|
||||
m_pLabel_Logo->setPixmap(logo);
|
||||
}
|
||||
|
||||
@ -121,7 +121,7 @@
|
||||
<string notr="true"/>
|
||||
</property>
|
||||
<property name="pixmap">
|
||||
<pixmap resource="../res/Synergy.qrc">:/res/image/about-light.png</pixmap>
|
||||
<pixmap>:/image/about-light.png</pixmap>
|
||||
</property>
|
||||
<property name="scaledContents">
|
||||
<bool>true</bool>
|
||||
@ -249,9 +249,6 @@
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<resources>
|
||||
<include location="../res/Synergy.qrc"/>
|
||||
</resources>
|
||||
<connections>
|
||||
<connection>
|
||||
<sender>buttonOk</sender>
|
||||
|
||||
@ -38,10 +38,14 @@
|
||||
#include "gui/styles.h"
|
||||
#include "gui/tls/TlsFingerprint.h"
|
||||
#include "license/License.h"
|
||||
#include "platform/wayland.h"
|
||||
|
||||
#if defined(Q_OS_MAC)
|
||||
#include "gui/OSXHelpers.h"
|
||||
#endif
|
||||
#if defined(Q_OS_LINUX)
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#include <QApplication>
|
||||
#include <QDesktopServices>
|
||||
@ -69,11 +73,11 @@ using CoreMode = CoreProcess::Mode;
|
||||
using CoreConnectionState = CoreProcess::ConnectionState;
|
||||
using CoreProcessState = CoreProcess::ProcessState;
|
||||
|
||||
const auto kIconFile16 = ":/res/icons/16x16/synergy.png";
|
||||
const auto kIconFile16 = ":/icons/16x16/synergy.png";
|
||||
|
||||
#ifdef Q_OS_MAC
|
||||
const auto kLightIconFile = ":/res/icons/64x64/synergy-light.png";
|
||||
const auto kDarkIconFile = ":/res/icons/64x64/synergy-dark.png";
|
||||
const auto kLightIconFile = ":/icons/64x64/synergy-light.png";
|
||||
const auto kDarkIconFile = ":/icons/64x64/synergy-dark.png";
|
||||
#endif // Q_OS_MAC
|
||||
|
||||
MainWindow::MainWindow(ConfigScopes &configScopes, AppConfig &appConfig)
|
||||
@ -584,6 +588,12 @@ void MainWindow::onCoreProcessStarting() {
|
||||
}
|
||||
}
|
||||
|
||||
#if defined(WINAPI_XWINDOWS) or defined(WINAPI_LIBEI)
|
||||
if (synergy::platform::isWayland()) {
|
||||
m_WaylandWarnings.showOnce(this, m_CoreProcess.mode());
|
||||
}
|
||||
#endif
|
||||
|
||||
saveSettings();
|
||||
}
|
||||
|
||||
@ -844,6 +854,10 @@ void MainWindow::updateStatus() {
|
||||
setStatus("Synergy is starting...");
|
||||
break;
|
||||
|
||||
case RetryPending:
|
||||
setStatus("Synergy will retry in a moment...");
|
||||
break;
|
||||
|
||||
case Stopping:
|
||||
setStatus("Synergy is stopping...");
|
||||
break;
|
||||
@ -896,7 +910,8 @@ void MainWindow::onCoreProcessStateChanged(CoreProcessState state) {
|
||||
}
|
||||
|
||||
if (state == CoreProcessState::Started ||
|
||||
state == CoreProcessState::Starting) {
|
||||
state == CoreProcessState::Starting ||
|
||||
state == CoreProcessState::RetryPending) {
|
||||
disconnect(
|
||||
m_pButtonToggleStart, &QPushButton::clicked, m_pActionStartCore,
|
||||
&QAction::trigger);
|
||||
|
||||
@ -36,6 +36,7 @@
|
||||
#include "gui/core/ClientConnection.h"
|
||||
#include "gui/core/CoreProcess.h"
|
||||
#include "gui/core/ServerConnection.h"
|
||||
#include "gui/core/WaylandWarnings.h"
|
||||
#include "gui/tls/TlsUtility.h"
|
||||
#include "ui_MainWindowBase.h"
|
||||
|
||||
@ -198,6 +199,7 @@ private:
|
||||
bool m_Quitting = false;
|
||||
synergy::gui::config::ServerConfigDialogState m_ServerConfigDialogState;
|
||||
bool m_SaveOnExit = true;
|
||||
synergy::gui::core::WaylandWarnings m_WaylandWarnings;
|
||||
|
||||
synergy::gui::ConfigScopes &m_ConfigScopes;
|
||||
AppConfig &m_AppConfig;
|
||||
|
||||
@ -501,7 +501,7 @@
|
||||
<string/>
|
||||
</property>
|
||||
<property name="pixmap">
|
||||
<pixmap resource="../res/Synergy.qrc">:/res/icons/16x16/padlock.png</pixmap>
|
||||
<pixmap>:/icons/16x16/padlock.png</pixmap>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
@ -740,9 +740,6 @@
|
||||
<tabstop>m_pButtonApply</tabstop>
|
||||
<tabstop>m_pButtonToggleStart</tabstop>
|
||||
</tabstops>
|
||||
<resources>
|
||||
<include location="../res/Synergy.qrc"/>
|
||||
</resources>
|
||||
<connections>
|
||||
<connection>
|
||||
<sender>m_pButtonToggleStart</sender>
|
||||
|
||||
@ -71,7 +71,7 @@
|
||||
<string/>
|
||||
</property>
|
||||
<property name="pixmap">
|
||||
<pixmap resource="../res/Synergy.qrc">:/res/icons/64x64/user-trash.png</pixmap>
|
||||
<pixmap>:/icons/64x64/user-trash.png</pixmap>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
@ -1072,8 +1072,8 @@ Enabling this setting will disable the server config GUI.</string>
|
||||
<string/>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset resource="../res/Synergy.qrc">
|
||||
<normaloff>:/res/icons/64x64/folder.png</normaloff>:/res/icons/64x64/folder.png</iconset>
|
||||
<iconset>
|
||||
<normaloff>:/icons/64x64/folder.png</normaloff>:/icons/64x64/folder.png</iconset>
|
||||
</property>
|
||||
<property name="iconSize">
|
||||
<size>
|
||||
@ -1179,9 +1179,6 @@ Enabling this setting will disable the server config GUI.</string>
|
||||
<tabstop>m_pEditConfigFile</tabstop>
|
||||
<tabstop>m_pTabWidget</tabstop>
|
||||
</tabstops>
|
||||
<resources>
|
||||
<include location="../res/Synergy.qrc"/>
|
||||
</resources>
|
||||
<connections>
|
||||
<connection>
|
||||
<sender>m_pButtonBox</sender>
|
||||
|
||||
@ -60,7 +60,7 @@
|
||||
<string/>
|
||||
</property>
|
||||
<property name="pixmap">
|
||||
<pixmap resource="../res/Synergy.qrc">:/res/image/welcome.png</pixmap>
|
||||
<pixmap>:/image/welcome.png</pixmap>
|
||||
</property>
|
||||
<property name="margin">
|
||||
<number>1</number>
|
||||
@ -186,8 +186,5 @@
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<resources>
|
||||
<include location="../res/Synergy.qrc"/>
|
||||
</resources>
|
||||
<connections/>
|
||||
</ui>
|
||||
|
||||
@ -15,6 +15,9 @@
|
||||
|
||||
set(target gui)
|
||||
|
||||
set(res_dir ${CMAKE_SOURCE_DIR}/res/gui)
|
||||
set(qrc_file ${res_dir}/Synergy.qrc)
|
||||
|
||||
set(CMAKE_AUTOMOC ON)
|
||||
set(CMAKE_AUTORCC ON)
|
||||
set(CMAKE_AUTOUIC ON)
|
||||
@ -33,7 +36,8 @@ if(ADD_HEADERS_TO_SOURCES)
|
||||
list(APPEND sources ${headers})
|
||||
endif()
|
||||
|
||||
add_library(${target} STATIC ${sources} ${ui_files})
|
||||
add_library(${target} STATIC ${sources} ${ui_files} ${qrc_file})
|
||||
|
||||
target_link_libraries(
|
||||
${target}
|
||||
license
|
||||
|
||||
@ -76,6 +76,8 @@ QString processStateToString(CoreProcess::ProcessState state) {
|
||||
return "stopping";
|
||||
case Stopped:
|
||||
return "stopped";
|
||||
case RetryPending:
|
||||
return "retry pending";
|
||||
default:
|
||||
qFatal("invalid process state");
|
||||
abort();
|
||||
@ -184,6 +186,14 @@ CoreProcess::CoreProcess(
|
||||
connect(
|
||||
&m_pDeps->process(), &QProcessProxy::readyReadStandardError, this,
|
||||
&CoreProcess::onProcessReadyReadStandardError);
|
||||
|
||||
connect(&m_retryTimer, &QTimer::timeout, [this] {
|
||||
if (m_processState == ProcessState::RetryPending) {
|
||||
start();
|
||||
} else {
|
||||
qDebug("retry cancelled, process state is not retry pending");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void CoreProcess::onIpcClientServiceReady() {
|
||||
@ -227,7 +237,6 @@ void CoreProcess::onProcessReadyReadStandardError() {
|
||||
void CoreProcess::onProcessFinished(int exitCode, QProcess::ExitStatus) {
|
||||
const auto wasStarted = m_processState == ProcessState::Started;
|
||||
|
||||
setProcessState(ProcessState::Stopped);
|
||||
setConnectionState(ConnectionState::Disconnected);
|
||||
|
||||
if (exitCode == 0) {
|
||||
@ -238,7 +247,16 @@ void CoreProcess::onProcessFinished(int exitCode, QProcess::ExitStatus) {
|
||||
|
||||
if (wasStarted) {
|
||||
qDebug("desktop process was running, retrying in %d ms", kRetryDelay);
|
||||
QTimer::singleShot(kRetryDelay, [this] { start(); });
|
||||
|
||||
if (m_retryTimer.isActive()) {
|
||||
m_retryTimer.stop();
|
||||
}
|
||||
|
||||
setProcessState(ProcessState::RetryPending);
|
||||
m_retryTimer.setSingleShot(true);
|
||||
m_retryTimer.start(kRetryDelay);
|
||||
} else {
|
||||
setProcessState(ProcessState::Stopped);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -27,6 +27,7 @@
|
||||
#include <QObject>
|
||||
#include <QString>
|
||||
#include <QStringList>
|
||||
#include <QTimer>
|
||||
#include <memory>
|
||||
|
||||
namespace synergy::gui {
|
||||
@ -55,7 +56,13 @@ public:
|
||||
|
||||
enum class Mode { None, Client, Server };
|
||||
enum class Error { AddressMissing, StartFailed };
|
||||
enum class ProcessState { Starting, Started, Stopping, Stopped };
|
||||
enum class ProcessState {
|
||||
Starting,
|
||||
Started,
|
||||
Stopping,
|
||||
Stopped,
|
||||
RetryPending
|
||||
};
|
||||
enum class ConnectionState { Disconnected, Connecting, Connected, Listening };
|
||||
|
||||
explicit CoreProcess(
|
||||
@ -130,6 +137,7 @@ private:
|
||||
QMutex m_processMutex;
|
||||
QString m_secureSocketVersion = "";
|
||||
std::optional<ProcessMode> m_lastProcessMode = std::nullopt;
|
||||
QTimer m_retryTimer;
|
||||
};
|
||||
|
||||
} // namespace synergy::gui
|
||||
|
||||
65
src/lib/gui/core/WaylandWarnings.cpp
Normal file
@ -0,0 +1,65 @@
|
||||
/*
|
||||
* synergy -- mouse and keyboard sharing utility
|
||||
* Copyright (C) 2024 Symless Ltd.
|
||||
*
|
||||
* This package is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* found in the file LICENSE that should have accompanied this file.
|
||||
*
|
||||
* This package is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "WaylandWarnings.h"
|
||||
|
||||
#include "messages.h"
|
||||
|
||||
using namespace synergy::platform;
|
||||
|
||||
namespace synergy::gui::core {
|
||||
|
||||
//
|
||||
// WaylandWarnings::Deps
|
||||
//
|
||||
|
||||
void WaylandWarnings::Deps::showWaylandLibraryError(QWidget *parent) {
|
||||
messages::showWaylandLibraryError(parent);
|
||||
}
|
||||
|
||||
void WaylandWarnings::Deps::showWaylandExperimental(QWidget *parent) {
|
||||
messages::showWaylandExperimental(parent);
|
||||
}
|
||||
|
||||
//
|
||||
// WaylandWarnings
|
||||
//
|
||||
|
||||
void WaylandWarnings::showOnce(
|
||||
QWidget *parent, CoreProcess::Mode mode, bool hasEi, bool hasPortal,
|
||||
bool hasPortalInputCapture) {
|
||||
|
||||
const auto portalIcProblem =
|
||||
!hasPortalInputCapture && mode == CoreProcess::Mode::Server;
|
||||
|
||||
if (!hasEi || !hasPortal || portalIcProblem) {
|
||||
if (!m_errorShown) {
|
||||
m_errorShown = true;
|
||||
m_pDeps->showWaylandLibraryError(parent);
|
||||
} else {
|
||||
qWarning("missing required wayland lib(s) or feature");
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (!m_warningShown) {
|
||||
m_warningShown = true;
|
||||
m_pDeps->showWaylandExperimental(parent);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace synergy::gui::core
|
||||
49
src/lib/gui/core/WaylandWarnings.h
Normal file
@ -0,0 +1,49 @@
|
||||
/*
|
||||
* synergy -- mouse and keyboard sharing utility
|
||||
* Copyright (C) 2024 Symless Ltd.
|
||||
*
|
||||
* This package is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* found in the file LICENSE that should have accompanied this file.
|
||||
*
|
||||
* This package is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <QWidget>
|
||||
#include <memory>
|
||||
|
||||
#include "CoreProcess.h"
|
||||
#include "platform/wayland.h"
|
||||
|
||||
namespace synergy::gui::core {
|
||||
|
||||
class WaylandWarnings {
|
||||
public:
|
||||
struct Deps {
|
||||
virtual ~Deps() = default;
|
||||
virtual void showWaylandExperimental(QWidget *parent);
|
||||
virtual void showWaylandLibraryError(QWidget *parent);
|
||||
};
|
||||
|
||||
explicit WaylandWarnings(
|
||||
std::shared_ptr<Deps> deps = std::make_shared<Deps>())
|
||||
: m_pDeps(deps) {}
|
||||
|
||||
void showOnce(
|
||||
QWidget *parent, CoreProcess::Mode mode, bool hasEi = platform::kHasEi,
|
||||
bool hasPortal = platform::kHasPortal,
|
||||
bool hasPortalInputCapture = platform::kHasPortalInputCapture);
|
||||
|
||||
private:
|
||||
bool m_errorShown{false};
|
||||
bool m_warningShown{false};
|
||||
std::shared_ptr<Deps> m_pDeps;
|
||||
};
|
||||
|
||||
} // namespace synergy::gui::core
|
||||
@ -277,8 +277,8 @@
|
||||
<string/>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset resource="../res/Synergy.qrc">
|
||||
<normaloff>:/res/icons/64x64/folder.png</normaloff>:/res/icons/64x64/folder.png</iconset>
|
||||
<iconset>
|
||||
<normaloff>:/icons/64x64/folder.png</normaloff>:/icons/64x64/folder.png</iconset>
|
||||
</property>
|
||||
<property name="iconSize">
|
||||
<size>
|
||||
@ -567,8 +567,8 @@
|
||||
<string/>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset resource="../res/Synergy.qrc">
|
||||
<normaloff>:/res/icons/64x64/folder.png</normaloff>:/res/icons/64x64/folder.png</iconset>
|
||||
<iconset>
|
||||
<normaloff>:/icons/64x64/folder.png</normaloff>:/icons/64x64/folder.png</iconset>
|
||||
</property>
|
||||
<property name="iconSize">
|
||||
<size>
|
||||
@ -753,9 +753,6 @@
|
||||
<tabstop>m_pRadioSystemScope</tabstop>
|
||||
<tabstop>m_pComboElevate</tabstop>
|
||||
</tabstops>
|
||||
<resources>
|
||||
<include location="../res/Synergy.qrc"/>
|
||||
</resources>
|
||||
<connections>
|
||||
<connection>
|
||||
<sender>m_pButtonBox</sender>
|
||||
|
||||
@ -288,4 +288,30 @@ void showReadOnlySettings(QWidget *parent, const QString &systemSettingsPath) {
|
||||
.arg(nativePath));
|
||||
}
|
||||
|
||||
void showWaylandExperimental(QWidget *parent) {
|
||||
QMessageBox::information(
|
||||
parent, "Experimental feature",
|
||||
QString(
|
||||
"<p>Thanks for trying the beta version!</p>"
|
||||
"<p>Wayland support is experimental and contains bugs.</p>"
|
||||
R"(<p>Please <a href="%1" style="color: %2">report bugs</a> to us if you find any.</p>)"
|
||||
"<p>Happy testing!</p>")
|
||||
.arg(kUrlBugReport, kColorSecondary));
|
||||
}
|
||||
|
||||
void showWaylandLibraryError(QWidget *parent) {
|
||||
QMessageBox::critical(
|
||||
parent, "Library problem",
|
||||
QString(
|
||||
"<p>Sorry, while this version of Synergy does support Wayland, "
|
||||
"this build was not linked with one or more of the required "
|
||||
"libraries.</p>"
|
||||
"<p>Please either switch to X from your login screen or use a build "
|
||||
"that uses the correct libraries.</p>"
|
||||
"<p>If you think this is incorrect, please "
|
||||
R"(<a href="%1" style="color: %2">report a bug</a>.</p>)"
|
||||
"<p>Please check the logs for more information.</p>")
|
||||
.arg(kUrlBugReport, kColorSecondary));
|
||||
}
|
||||
|
||||
} // namespace synergy::gui::messages
|
||||
|
||||
@ -52,4 +52,8 @@ bool showClearSettings(QWidget *parent);
|
||||
|
||||
void showReadOnlySettings(QWidget *parent, const QString &systemSettingsPath);
|
||||
|
||||
void showWaylandExperimental(QWidget *parent);
|
||||
|
||||
void showWaylandLibraryError(QWidget *parent);
|
||||
|
||||
} // namespace synergy::gui::messages
|
||||
|
||||
@ -71,8 +71,7 @@ EiScreen::EiScreen(bool is_primary, IEventQueue *events, bool use_portal)
|
||||
#if HAVE_LIBPORTAL_INPUTCAPTURE
|
||||
portal_input_capture_ = new PortalInputCapture(this, events_);
|
||||
#else
|
||||
throw std::invalid_argument(
|
||||
"Missing libportal InputCapture portal support");
|
||||
throw std::invalid_argument("missing libportal input capture support");
|
||||
#endif // HAVE_LIBPORTAL_INPUTCAPTURE
|
||||
} else {
|
||||
#if WINAPI_LIBPORTAL
|
||||
@ -82,8 +81,7 @@ EiScreen::EiScreen(bool is_primary, IEventQueue *events, bool use_portal)
|
||||
this, &EiScreen::handle_portal_session_closed));
|
||||
portal_remote_desktop_ = new PortalRemoteDesktop(this, events_);
|
||||
#else
|
||||
throw std::invalid_argument(
|
||||
"Missing libportal RemoteDesktop portal support");
|
||||
throw std::invalid_argument("missing libportal remote desktop support");
|
||||
#endif // WINAPI_LIBPORTAL
|
||||
}
|
||||
} else {
|
||||
@ -91,7 +89,7 @@ EiScreen::EiScreen(bool is_primary, IEventQueue *events, bool use_portal)
|
||||
auto rc = ei_setup_backend_socket(ei_, nullptr);
|
||||
if (rc != 0) {
|
||||
LOG_DEBUG("ei init error: %s", strerror(-rc));
|
||||
throw std::runtime_error("Failed to init ei context");
|
||||
throw std::runtime_error("failed to init ei context");
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -819,7 +817,7 @@ void EiScreen::updateButtons() {
|
||||
IKeyState *EiScreen::getKeyState() const { return key_state_; }
|
||||
|
||||
String EiScreen::getSecureInputApp() const {
|
||||
throw std::runtime_error("Not implemented");
|
||||
throw std::runtime_error("get security input app not implemented");
|
||||
}
|
||||
|
||||
EiScreen::HotKeyItem::HotKeyItem(std::uint32_t mask, std::uint32_t id)
|
||||
|
||||
@ -130,7 +130,7 @@ void PortalInputCapture::cb_session_closed(XdpSession *session) {
|
||||
|
||||
void PortalInputCapture::cb_init_input_capture_session(
|
||||
GObject *object, GAsyncResult *res) {
|
||||
LOG_DEBUG("portal session ready");
|
||||
LOG_DEBUG("portal input capture session initialized");
|
||||
g_autoptr(GError) error = nullptr;
|
||||
|
||||
auto session = xdp_portal_create_input_capture_session_finish(
|
||||
|
||||
8
src/lib/platform/PortalRemoteDesktop.cpp
Normal file → Executable file
@ -94,7 +94,9 @@ void PortalRemoteDesktop::cb_session_started(
|
||||
auto session = XDP_SESSION(object);
|
||||
auto success = xdp_session_start_finish(session, res, &error);
|
||||
if (!success) {
|
||||
LOG_ERR("failed to start portal remote desktop session");
|
||||
LOG_ERR(
|
||||
"failed to start portal remote desktop session, quitting: %s",
|
||||
error->message);
|
||||
g_main_loop_quit(glib_main_loop_);
|
||||
events_->addEvent(Event::kQuit);
|
||||
return;
|
||||
@ -123,7 +125,7 @@ void PortalRemoteDesktop::cb_session_started(
|
||||
|
||||
void PortalRemoteDesktop::cb_init_remote_desktop_session(
|
||||
GObject *object, GAsyncResult *res) {
|
||||
LOG_DEBUG("remote desktop session ready");
|
||||
LOG_DEBUG("portal remote desktop session initialized");
|
||||
g_autoptr(GError) error = nullptr;
|
||||
|
||||
auto session = xdp_portal_create_remote_desktop_session_finish(
|
||||
@ -149,7 +151,7 @@ void PortalRemoteDesktop::cb_init_remote_desktop_session(
|
||||
session_signal_id_ = g_signal_connect(
|
||||
G_OBJECT(session), "closed", G_CALLBACK(cb_session_closed_cb), this);
|
||||
|
||||
LOG_DEBUG("Session ready, starting");
|
||||
LOG_DEBUG("portal remote desktop session starting");
|
||||
xdp_session_start(
|
||||
session,
|
||||
nullptr, // parent
|
||||
|
||||
@ -21,9 +21,27 @@
|
||||
|
||||
namespace synergy::platform {
|
||||
|
||||
#if WINAPI_LIBEI
|
||||
const auto kHasEi = true;
|
||||
#else
|
||||
const auto kHasEi = false;
|
||||
#endif
|
||||
|
||||
#if WINAPI_LIBPORTAL
|
||||
const auto kHasPortal = true;
|
||||
#else
|
||||
const auto kHasPortal = false;
|
||||
#endif
|
||||
|
||||
#if HAVE_LIBPORTAL_INPUTCAPTURE
|
||||
const auto kHasPortalInputCapture = true;
|
||||
#else
|
||||
const auto kHasPortalInputCapture = false;
|
||||
#endif
|
||||
|
||||
inline bool isWayland() {
|
||||
const std::string session = getenv("XDG_SESSION_TYPE");
|
||||
return session == "wayland";
|
||||
const auto session = getenv("XDG_SESSION_TYPE");
|
||||
return session != nullptr && std::string(session) == "wayland";
|
||||
}
|
||||
|
||||
} // namespace synergy::platform
|
||||
|
||||
@ -31,6 +31,8 @@
|
||||
#include "synergy/unix/AppUtilUnix.h"
|
||||
#endif
|
||||
|
||||
#include <stdexcept>
|
||||
|
||||
class IArchTaskBarReceiver;
|
||||
class BufferedLogOutputter;
|
||||
class ILogOutputter;
|
||||
@ -46,6 +48,11 @@ typedef IArchTaskBarReceiver *(*CreateTaskBarReceiverFunc)(
|
||||
|
||||
class App : public IApp {
|
||||
public:
|
||||
class XNoEiSupport : public std::runtime_error {
|
||||
public:
|
||||
XNoEiSupport() : std::runtime_error("libei is not supported") {}
|
||||
};
|
||||
|
||||
App(IEventQueue *events, CreateTaskBarReceiverFunc createTaskBarReceiver,
|
||||
synergy::ArgsBase *args);
|
||||
App(App const &) = delete;
|
||||
|
||||
@ -186,13 +186,19 @@ synergy::Screen *ClientApp::createScreen() {
|
||||
args().m_enableLangSync, args().m_clientScrollDirection),
|
||||
m_events);
|
||||
#endif
|
||||
#if WINAPI_LIBEI
|
||||
|
||||
#if defined(WINAPI_XWINDOWS) or defined(WINAPI_LIBEI)
|
||||
if (synergy::platform::isWayland()) {
|
||||
#if WINAPI_LIBEI
|
||||
LOG((CLOG_INFO "using ei screen for wayland"));
|
||||
return new synergy::Screen(
|
||||
new synergy::EiScreen(false, m_events, true), m_events);
|
||||
#else
|
||||
throw XNoEiSupport();
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
|
||||
#if WINAPI_XWINDOWS
|
||||
LOG((CLOG_INFO "using legacy x windows screen"));
|
||||
return new synergy::Screen(
|
||||
@ -202,6 +208,7 @@ synergy::Screen *ClientApp::createScreen() {
|
||||
m_events);
|
||||
|
||||
#endif
|
||||
|
||||
#if WINAPI_CARBON
|
||||
return new synergy::Screen(
|
||||
new OSXScreen(
|
||||
|
||||
@ -560,11 +560,15 @@ synergy::Screen *ServerApp::createScreen() {
|
||||
m_events);
|
||||
#endif
|
||||
|
||||
#if WINAPI_LIBEI
|
||||
#if defined(WINAPI_XWINDOWS) or defined(WINAPI_LIBEI)
|
||||
if (synergy::platform::isWayland()) {
|
||||
#if WINAPI_LIBEI
|
||||
LOG((CLOG_INFO "using ei screen for wayland"));
|
||||
return new synergy::Screen(
|
||||
new synergy::EiScreen(true, m_events, true), m_events);
|
||||
#else
|
||||
throw XNoEiSupport();
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
129
src/test/unittests/gui/core/WaylandWarningsTests.cpp
Normal file
@ -0,0 +1,129 @@
|
||||
/*
|
||||
* synergy -- mouse and keyboard sharing utility
|
||||
* Copyright (C) 2024 Symless Ltd.
|
||||
*
|
||||
* This package is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* found in the file LICENSE that should have accompanied this file.
|
||||
*
|
||||
* This package is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "gui/core/WaylandWarnings.h"
|
||||
|
||||
#include "gui/core/CoreProcess.h"
|
||||
|
||||
#include <gmock/gmock.h>
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
using namespace synergy::gui;
|
||||
using namespace synergy::gui::core;
|
||||
|
||||
namespace {
|
||||
struct MockDeps : public WaylandWarnings::Deps {
|
||||
MOCK_METHOD(void, showWaylandExperimental, (QWidget *), (override));
|
||||
MOCK_METHOD(void, showWaylandLibraryError, (QWidget *), (override));
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
TEST(WaylandWarningsTests, showOnce_serverNoEi_showLibraryError) {
|
||||
const auto deps = std::make_shared<MockDeps>();
|
||||
const bool hasEi = false;
|
||||
const bool hasPortal = false;
|
||||
const bool hasPortalIC = false;
|
||||
WaylandWarnings waylandWarnings(deps);
|
||||
|
||||
EXPECT_CALL(*deps, showWaylandLibraryError(nullptr)).Times(1);
|
||||
|
||||
waylandWarnings.showOnce(
|
||||
nullptr, CoreProcess::Mode::Server, hasEi, hasPortal, hasPortalIC);
|
||||
}
|
||||
|
||||
TEST(WaylandWarningsTests, showOnce_serverNoPortal_showLibraryError) {
|
||||
const auto deps = std::make_shared<MockDeps>();
|
||||
const bool hasEi = true;
|
||||
const bool hasPortal = false;
|
||||
const bool hasPortalIC = false;
|
||||
WaylandWarnings waylandWarnings(deps);
|
||||
|
||||
EXPECT_CALL(*deps, showWaylandLibraryError(nullptr)).Times(1);
|
||||
|
||||
waylandWarnings.showOnce(
|
||||
nullptr, CoreProcess::Mode::Server, hasEi, hasPortal, hasPortalIC);
|
||||
}
|
||||
|
||||
TEST(WaylandWarningsTests, showOnce_serverNoPortalIc_showLibraryError) {
|
||||
const auto deps = std::make_shared<MockDeps>();
|
||||
const bool hasEi = true;
|
||||
const bool hasPortal = true;
|
||||
const bool hasPortalIC = false;
|
||||
WaylandWarnings waylandWarnings(deps);
|
||||
|
||||
EXPECT_CALL(*deps, showWaylandLibraryError(nullptr)).Times(1);
|
||||
|
||||
waylandWarnings.showOnce(
|
||||
nullptr, CoreProcess::Mode::Server, hasEi, hasPortal, hasPortalIC);
|
||||
}
|
||||
|
||||
TEST(WaylandWarningTests, showOnce_clientNoPortalIc_showExperimental) {
|
||||
const auto deps = std::make_shared<MockDeps>();
|
||||
const bool hasEi = true;
|
||||
const bool hasPortal = true;
|
||||
const bool hasPortalIC = false;
|
||||
WaylandWarnings waylandWarnings(deps);
|
||||
|
||||
EXPECT_CALL(*deps, showWaylandExperimental(nullptr)).Times(1);
|
||||
|
||||
waylandWarnings.showOnce(
|
||||
nullptr, CoreProcess::Mode::Client, hasEi, hasPortal, hasPortalIC);
|
||||
}
|
||||
|
||||
TEST(WaylandWarningsTests, showOnce_serverHasPortalIc_showExperimentalOnly) {
|
||||
const auto deps = std::make_shared<MockDeps>();
|
||||
const bool hasEi = true;
|
||||
const bool hasPortal = true;
|
||||
const bool hasPortalIC = true;
|
||||
WaylandWarnings waylandWarnings(deps);
|
||||
|
||||
EXPECT_CALL(*deps, showWaylandExperimental(nullptr)).Times(1);
|
||||
|
||||
waylandWarnings.showOnce(
|
||||
nullptr, CoreProcess::Mode::Server, hasEi, hasPortal, hasPortalIC);
|
||||
}
|
||||
|
||||
TEST(WaylandWarningsTests, showOnce_failureCalledTwice_messageOnlyShownOnce) {
|
||||
const auto deps = std::make_shared<MockDeps>();
|
||||
const bool hasEi = false;
|
||||
const bool hasPortal = false;
|
||||
const bool hasPortalIC = false;
|
||||
WaylandWarnings waylandWarnings(deps);
|
||||
|
||||
EXPECT_CALL(*deps, showWaylandLibraryError(nullptr)).Times(1);
|
||||
|
||||
waylandWarnings.showOnce(
|
||||
nullptr, CoreProcess::Mode::Server, hasEi, hasPortal, hasPortalIC);
|
||||
waylandWarnings.showOnce(
|
||||
nullptr, CoreProcess::Mode::Server, hasEi, hasPortal, hasPortalIC);
|
||||
}
|
||||
|
||||
TEST(WaylandWarningsTests, showOnce_successCalledTwice_messageOnlyShownOnce) {
|
||||
const auto deps = std::make_shared<MockDeps>();
|
||||
const bool hasEi = true;
|
||||
const bool hasPortal = true;
|
||||
const bool hasPortalIC = true;
|
||||
WaylandWarnings waylandWarnings(deps);
|
||||
|
||||
EXPECT_CALL(*deps, showWaylandExperimental(nullptr)).Times(1);
|
||||
|
||||
waylandWarnings.showOnce(
|
||||
nullptr, CoreProcess::Mode::Server, hasEi, hasPortal, hasPortalIC);
|
||||
waylandWarnings.showOnce(
|
||||
nullptr, CoreProcess::Mode::Server, hasEi, hasPortal, hasPortalIC);
|
||||
}
|
||||