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
This commit is contained in:
Nick Bolton
2024-09-09 11:18:17 +01:00
committed by GitHub
parent 59280bb124
commit 8beeaf4276
45 changed files with 405 additions and 62 deletions

View File

@ -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

View File

@ -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

View File

@ -25,6 +25,7 @@
"hotspots",
"Hutterer",
"ifdef",
"INPUTCAPTURE",
"integtests",
"keychain",
"Keychains",
@ -69,7 +70,8 @@
"vmactions",
"Volker",
"whot",
"winget"
"winget",
"XWINDOWS"
],
"ignoreWords": [],
"import": []

View File

@ -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>

View File

Before

Width:  |  Height:  |  Size: 450 B

After

Width:  |  Height:  |  Size: 450 B

View File

Before

Width:  |  Height:  |  Size: 905 B

After

Width:  |  Height:  |  Size: 905 B

View File

Before

Width:  |  Height:  |  Size: 693 B

After

Width:  |  Height:  |  Size: 693 B

View File

Before

Width:  |  Height:  |  Size: 99 KiB

After

Width:  |  Height:  |  Size: 99 KiB

View File

Before

Width:  |  Height:  |  Size: 919 B

After

Width:  |  Height:  |  Size: 919 B

View File

Before

Width:  |  Height:  |  Size: 1.0 KiB

After

Width:  |  Height:  |  Size: 1.0 KiB

View File

Before

Width:  |  Height:  |  Size: 1.7 KiB

After

Width:  |  Height:  |  Size: 1.7 KiB

View File

Before

Width:  |  Height:  |  Size: 3.7 KiB

After

Width:  |  Height:  |  Size: 3.7 KiB

View File

Before

Width:  |  Height:  |  Size: 2.5 KiB

After

Width:  |  Height:  |  Size: 2.5 KiB

View File

Before

Width:  |  Height:  |  Size: 6.1 KiB

After

Width:  |  Height:  |  Size: 6.1 KiB

View File

Before

Width:  |  Height:  |  Size: 7.4 KiB

After

Width:  |  Height:  |  Size: 7.4 KiB

View File

Before

Width:  |  Height:  |  Size: 12 KiB

After

Width:  |  Height:  |  Size: 12 KiB

View File

Before

Width:  |  Height:  |  Size: 11 KiB

After

Width:  |  Height:  |  Size: 11 KiB

View File

@ -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)

View File

@ -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);
}

View File

@ -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>

View File

@ -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);

View File

@ -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;

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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

View File

@ -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);
}
}

View File

@ -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

View 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

View 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

View File

@ -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>

View File

@ -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

View File

@ -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

View File

@ -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)

View File

@ -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
View 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

View File

@ -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

View File

@ -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;

View File

@ -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(

View File

@ -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

View 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);
}