Refactor settings dialog to simplify enable/disable logic (#7428)

* Move screen config files to new GUI lib

* Fixed bugs in config dialog related to service checkbox

* Test status tip

* Revert "Test status tip"

This reverts commit d11ad5bc22dab7edcf7ef0b3baada2fce240398a.

* Explain why service disabled

* Corrected client error message (written by non-native English writer) to make the message easier to understand for native English speakers

* Clearer logic

* Improve readability

* Coverage for 'add client' and client error messages

* Coverage for `Screen` class

* Clarify test names

* Use `const QString &` for backward compatibility

* Fixed lint warnings

* Prevent return paths after qFatal

* Use auto and abort to solve lint warnings

* Remove unused fake error

* Make operator hidden friend

* Remove redundant access specifier

* Convert int explicitly

* Swap && logic for linter

* Update ChangeLog
This commit is contained in:
Nick Bolton 2024-08-08 18:41:59 +01:00 committed by GitHub
parent 6b1e7e6f1c
commit de21030892
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
52 changed files with 1188 additions and 604 deletions

View file

@ -71,6 +71,7 @@ Enhancements:
- #7425 Refactored core process management and IPC client
- #7426 Fixed warnings and enable errors as warnings
- #7427 More reliable button click detection for add client dialog
- #7428 Refactor settings dialog to simplify enable/disable logic
# 1.14.6

View file

@ -33,6 +33,7 @@
"Olena",
"outputdir",
"Petroules",
"Pixmap",
"Poschta",
"Priddy",
"runas",

View file

@ -94,8 +94,8 @@ MainWindow::MainWindow(ConfigScopes &configScopes, AppConfig &appConfig)
m_AppConfig(appConfig),
m_ServerConfig(appConfig, *this),
m_CoreProcess(appConfig, m_ServerConfig),
m_ServerConnection(*this, appConfig, m_ServerConfig),
m_ClientConnection(*this, appConfig),
m_ServerConnection(this, appConfig, m_ServerConfig),
m_ClientConnection(this, appConfig),
m_TlsUtility(appConfig, m_LicenseHandler.license()),
m_WindowSaveTimer(this) {
@ -251,7 +251,9 @@ void MainWindow::connectSlots() {
connect(m_pActionMinimize, &QAction::triggered, this, &MainWindow::hide);
connect(m_pActionRestore, &QAction::triggered, this, &MainWindow::showNormal);
connect(
m_pActionRestore, &QAction::triggered, //
[this]() { showAndActivate(); });
connect(
m_pActionStartCore, &QAction::triggered, this,
@ -278,6 +280,14 @@ void MainWindow::connectSlots() {
connect(
&m_ServerConnection, &ServerConnection::configureClient, this,
&MainWindow::onServerConnectionConfigureClient);
connect(
&m_ServerConnection, &ServerConnection::messageShowing, this,
[this]() { showAndActivate(); });
connect(
&m_ClientConnection, &ClientConnection::messageShowing, this,
[this]() { showAndActivate(); });
}
void MainWindow::onAppAboutToQuit() { m_ConfigScopes.save(); }
@ -339,8 +349,7 @@ void MainWindow::onTrayIconActivated(QSystemTrayIcon::ActivationReason reason) {
if (isVisible()) {
hide();
} else {
showNormal();
activateWindow();
showAndActivate();
}
}
}
@ -355,7 +364,7 @@ void MainWindow::onVersionCheckerUpdateFound(const QString &version) {
}
void MainWindow::onActionStartCoreTriggered() {
m_ClientConnection.setCheckConnection(true);
m_ClientConnection.setShowMessage();
m_CoreProcess.start();
}
@ -408,9 +417,11 @@ void MainWindow::on_m_pActionHelp_triggered() {
}
void MainWindow::on_m_pActionSettings_triggered() {
auto result =
SettingsDialog(this, m_AppConfig, m_LicenseHandler.license()).exec();
if (result == QDialog::Accepted) {
auto dialog = SettingsDialog(
this, m_AppConfig, m_ServerConfig, m_LicenseHandler.license(),
m_CoreProcess);
if (dialog.exec() == QDialog::Accepted) {
m_ConfigScopes.save();
applyConfig();
@ -445,7 +456,7 @@ void MainWindow::on_m_pLineEditClientIp_textChanged(const QString &text) {
}
void MainWindow::on_m_pButtonApply_clicked() {
m_ClientConnection.setCheckConnection(true);
m_ClientConnection.setShowMessage();
m_CoreProcess.restart();
}
@ -516,7 +527,7 @@ void MainWindow::open() {
if (m_AppConfig.autoHide()) {
hide();
} else {
showNormal();
showAndActivate();
}
m_VersionChecker.checkLatest();
@ -651,10 +662,10 @@ void MainWindow::updateFromLogLine(const QString &line) {
void MainWindow::checkConnected(const QString &line) {
if (m_pRadioGroupServer->isChecked()) {
m_ServerConnection.update(line);
m_ServerConnection.handleLogLine(line);
m_pLabelServerState->updateServerState(line);
} else {
m_ClientConnection.update(line);
m_ClientConnection.handleLogLine(line);
m_pLabelClientState->updateClientState(line);
}
}
@ -1071,3 +1082,13 @@ void MainWindow::enableClient(bool enable) {
m_CoreProcess.setMode(CoreProcess::Mode::Client);
}
}
void MainWindow::showAndActivate() {
if (!isMinimized() && !isHidden()) {
qDebug("window already visible");
return;
}
showNormal();
activateWindow();
}

View file

@ -171,6 +171,7 @@ private:
void showDevThanksMessage();
QString productName() const;
void updateStatus();
void showAndActivate();
VersionChecker m_VersionChecker;
synergy::gui::TrayIcon m_TrayIcon;

View file

@ -1,6 +1,6 @@
/*
* synergy -- mouse and keyboard sharing utility
* Copyright (C) 2012-2016 Symless Ltd.
* Copyright (C) 2012 Symless Ltd.
* Copyright (C) 2008 Volker Lanz (vl@fidra.de)
*
* This package is free software; you can redistribute it and/or
@ -17,7 +17,8 @@
*/
#include "ScreenSettingsDialog.h"
#include "Screen.h"
#include "gui/config/Screen.h"
#include "gui/styles.h"
#include "validators/AliasValidator.h"
#include "validators/ScreenNameValidator.h"
@ -28,6 +29,9 @@
#include <QtGui>
using namespace synergy::gui;
using enum ScreenConfig::Modifier;
using enum ScreenConfig::SwitchCorner;
using enum ScreenConfig::Fix;
ScreenSettingsDialog::ScreenSettingsDialog(
QWidget *parent, Screen *pScreen, const ScreenList *pScreens)
@ -53,26 +57,28 @@ ScreenSettingsDialog::ScreenSettingsDialog(
for (int i = 0; i < m_pScreen->aliases().count(); i++)
new QListWidgetItem(m_pScreen->aliases()[i], m_pListAliases);
m_pComboBoxShift->setCurrentIndex(m_pScreen->modifier(Screen::Shift));
m_pComboBoxCtrl->setCurrentIndex(m_pScreen->modifier(Screen::Ctrl));
m_pComboBoxAlt->setCurrentIndex(m_pScreen->modifier(Screen::Alt));
m_pComboBoxMeta->setCurrentIndex(m_pScreen->modifier(Screen::Meta));
m_pComboBoxSuper->setCurrentIndex(m_pScreen->modifier(Screen::Super));
m_pComboBoxShift->setCurrentIndex(
m_pScreen->modifier(static_cast<int>(Shift)));
m_pComboBoxCtrl->setCurrentIndex(m_pScreen->modifier(static_cast<int>(Ctrl)));
m_pComboBoxAlt->setCurrentIndex(m_pScreen->modifier(static_cast<int>(Alt)));
m_pComboBoxMeta->setCurrentIndex(m_pScreen->modifier(static_cast<int>(Meta)));
m_pComboBoxSuper->setCurrentIndex(
m_pScreen->modifier(static_cast<int>(Super)));
m_pCheckBoxCornerTopLeft->setChecked(
m_pScreen->switchCorner(Screen::TopLeft));
m_pScreen->switchCorner(static_cast<int>(TopLeft)));
m_pCheckBoxCornerTopRight->setChecked(
m_pScreen->switchCorner(Screen::TopRight));
m_pScreen->switchCorner(static_cast<int>(TopRight)));
m_pCheckBoxCornerBottomLeft->setChecked(
m_pScreen->switchCorner(Screen::BottomLeft));
m_pScreen->switchCorner(static_cast<int>(BottomLeft)));
m_pCheckBoxCornerBottomRight->setChecked(
m_pScreen->switchCorner(Screen::BottomRight));
m_pScreen->switchCorner(static_cast<int>(BottomRight)));
m_pSpinBoxSwitchCornerSize->setValue(m_pScreen->switchCornerSize());
m_pCheckBoxCapsLock->setChecked(m_pScreen->fix(Screen::CapsLock));
m_pCheckBoxNumLock->setChecked(m_pScreen->fix(Screen::NumLock));
m_pCheckBoxScrollLock->setChecked(m_pScreen->fix(Screen::ScrollLock));
m_pCheckBoxXTest->setChecked(m_pScreen->fix(Screen::XTest));
m_pCheckBoxCapsLock->setChecked(m_pScreen->fix(CapsLock));
m_pCheckBoxNumLock->setChecked(m_pScreen->fix(NumLock));
m_pCheckBoxScrollLock->setChecked(m_pScreen->fix(ScrollLock));
m_pCheckBoxXTest->setChecked(m_pScreen->fix(XTest));
}
void ScreenSettingsDialog::accept() {
@ -102,26 +108,32 @@ void ScreenSettingsDialog::accept() {
m_pScreen->addAlias(alias);
}
m_pScreen->setModifier(Screen::Shift, m_pComboBoxShift->currentIndex());
m_pScreen->setModifier(Screen::Ctrl, m_pComboBoxCtrl->currentIndex());
m_pScreen->setModifier(Screen::Alt, m_pComboBoxAlt->currentIndex());
m_pScreen->setModifier(Screen::Meta, m_pComboBoxMeta->currentIndex());
m_pScreen->setModifier(Screen::Super, m_pComboBoxSuper->currentIndex());
m_pScreen->setModifier(
static_cast<int>(Shift), m_pComboBoxShift->currentIndex());
m_pScreen->setModifier(
static_cast<int>(Ctrl), m_pComboBoxCtrl->currentIndex());
m_pScreen->setModifier(static_cast<int>(Alt), m_pComboBoxAlt->currentIndex());
m_pScreen->setModifier(
static_cast<int>(Meta), m_pComboBoxMeta->currentIndex());
m_pScreen->setModifier(
static_cast<int>(Super), m_pComboBoxSuper->currentIndex());
m_pScreen->setSwitchCorner(
Screen::TopLeft, m_pCheckBoxCornerTopLeft->isChecked());
static_cast<int>(TopLeft), m_pCheckBoxCornerTopLeft->isChecked());
m_pScreen->setSwitchCorner(
Screen::TopRight, m_pCheckBoxCornerTopRight->isChecked());
static_cast<int>(TopRight), m_pCheckBoxCornerTopRight->isChecked());
m_pScreen->setSwitchCorner(
Screen::BottomLeft, m_pCheckBoxCornerBottomLeft->isChecked());
static_cast<int>(BottomLeft), m_pCheckBoxCornerBottomLeft->isChecked());
m_pScreen->setSwitchCorner(
Screen::BottomRight, m_pCheckBoxCornerBottomRight->isChecked());
static_cast<int>(BottomRight), m_pCheckBoxCornerBottomRight->isChecked());
m_pScreen->setSwitchCornerSize(m_pSpinBoxSwitchCornerSize->value());
m_pScreen->setFix(Screen::CapsLock, m_pCheckBoxCapsLock->isChecked());
m_pScreen->setFix(Screen::NumLock, m_pCheckBoxNumLock->isChecked());
m_pScreen->setFix(Screen::ScrollLock, m_pCheckBoxScrollLock->isChecked());
m_pScreen->setFix(Screen::XTest, m_pCheckBoxXTest->isChecked());
m_pScreen->setFix(
static_cast<int>(CapsLock), m_pCheckBoxCapsLock->isChecked());
m_pScreen->setFix(static_cast<int>(NumLock), m_pCheckBoxNumLock->isChecked());
m_pScreen->setFix(
static_cast<int>(ScrollLock), m_pCheckBoxScrollLock->isChecked());
m_pScreen->setFix(static_cast<int>(XTest), m_pCheckBoxXTest->isChecked());
QDialog::accept();
}

View file

@ -1,6 +1,6 @@
/*
* synergy -- mouse and keyboard sharing utility
* Copyright (C) 2012-2016 Symless Ltd.
* Copyright (C) 2012 Symless Ltd.
* Copyright (C) 2008 Volker Lanz (vl@fidra.de)
*
* This package is free software; you can redistribute it and/or
@ -17,7 +17,8 @@
*/
#include "ScreenSetupModel.h"
#include "Screen.h"
#include "gui/config/Screen.h"
#include <QtCore>
#include <QtGui>
@ -137,7 +138,7 @@ bool ScreenSetupModel::dropMimeData(
Screen droppedScreen;
stream >> droppedScreen;
Screen oldScreen = screen(parent.column(), parent.row());
auto oldScreen = Screen(screen(parent.column(), parent.row()));
if (!oldScreen.isNull() && sourceColumn != -1 && sourceRow != -1) {
// mark the screen so it isn't deleted after the dragndrop succeeded
// see ScreenSetupView::startDrag()

View file

@ -1,6 +1,6 @@
/*
* synergy -- mouse and keyboard sharing utility
* Copyright (C) 2012-2016 Symless Ltd.
* Copyright (C) 2012 Symless Ltd.
* Copyright (C) 2008 Volker Lanz (vl@fidra.de)
*
* This package is free software; you can redistribute it and/or
@ -16,16 +16,14 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#if !defined(SCREENSETUPMODEL__H)
#define SCREENSETUPMODEL__H
#pragma once
#include <QAbstractTableModel>
#include <QList>
#include <QString>
#include <QStringList>
#include "ScreenList.h"
#include "gui/config/ScreenList.h"
class ScreenSetupView;
class ServerConfigDialog;
@ -80,5 +78,3 @@ private:
static const QString m_MimeType;
};
#endif

View file

@ -1,6 +1,6 @@
/*
* synergy -- mouse and keyboard sharing utility
* Copyright (C) 2012-2016 Symless Ltd.
* Copyright (C) 2012 Symless Ltd.
* Copyright (C) 2008 Volker Lanz (vl@fidra.de)
*
* This package is free software; you can redistribute it and/or
@ -17,6 +17,7 @@
*/
#include "ScreenSetupView.h"
#include "ScreenSettingsDialog.h"
#include "ScreenSetupModel.h"

View file

@ -1,6 +1,6 @@
/*
* synergy -- mouse and keyboard sharing utility
* Copyright (C) 2012-2016 Symless Ltd.
* Copyright (C) 2012 Symless Ltd.
* Copyright (C) 2008 Volker Lanz (vl@fidra.de)
*
* This package is free software; you can redistribute it and/or
@ -16,14 +16,12 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#if !defined(SCREENSETUPVIEW__H)
#define SCREENSETUPVIEW__H
#pragma once
#include <QFlags>
#include <QTableView>
#include "Screen.h"
#include "gui/config/Screen.h"
class QWidget;
class QMouseEvent;
@ -51,5 +49,3 @@ protected:
void initViewItemOption(QStyleOptionViewItem *option) const override;
void scrollTo(const QModelIndex &, ScrollHint) override {}
};
#endif

View file

@ -27,6 +27,11 @@
#include <QPushButton>
#include <QtCore>
using namespace synergy::gui::proxy;
using enum ScreenConfig::Modifier;
using enum ScreenConfig::SwitchCorner;
using enum ScreenConfig::Fix;
static const struct {
int x;
int y;
@ -94,7 +99,7 @@ void ServerConfig::setupScreens() {
hotkeys().clear();
// m_NumSwitchCorners is used as a fixed size array. See Screen::init()
for (int i = 0; i < NumSwitchCorners; i++)
for (int i = 0; i < static_cast<int>(NumSwitchCorners); i++)
switchCorners() << false;
// There must always be screen objects for each cell in the screens QList.
@ -147,7 +152,7 @@ void ServerConfig::commit() {
settings().beginWriteArray("hotkeys");
for (int i = 0; i < hotkeys().size(); i++) {
settings().setArrayIndex(i);
hotkeys()[i].saveSettings(settings());
hotkeys()[i].saveSettings(settings().get());
}
settings().endArray();
@ -189,7 +194,8 @@ void ServerConfig::recall() {
setClientAddress(settings().value("clientAddress", "").toString());
readSettings(
settings(), switchCorners(), "switchCorner", false, NumSwitchCorners);
settings(), switchCorners(), "switchCorner", 0,
static_cast<int>(NumSwitchCorners));
int numScreens = settings().beginReadArray("screens");
Q_ASSERT(numScreens <= screens().size());
@ -206,7 +212,7 @@ void ServerConfig::recall() {
for (int i = 0; i < numHotkeys; i++) {
settings().setArrayIndex(i);
Hotkey h;
h.loadSettings(settings());
h.loadSettings(settings().get());
hotkeys().append(h);
}
settings().endArray();
@ -449,7 +455,7 @@ void ServerConfig::addClient(const QString &clientName) {
fixNoServer(m_pAppConfig->screenName(), serverIndex);
}
m_Screens.addScreenByPriority(clientName);
m_Screens.addScreenByPriority(Screen(clientName));
}
void ServerConfig::setConfigFile(const QString &configFile) {
@ -542,6 +548,6 @@ QString ServerConfig::getClientAddress() const {
return clientAddress;
}
QSettings &ServerConfig::settings() {
return *m_pAppConfig->scopes().activeSettings();
QSettingsProxy &ServerConfig::settings() {
return m_pAppConfig->scopes().activeSettings();
}

View file

@ -1,6 +1,6 @@
/*
* synergy -- mouse and keyboard sharing utility
* Copyright (C) 2012-2016 Symless Ltd.
* Copyright (C) 2012 Symless Ltd.
* Copyright (C) 2008 Volker Lanz (vl@fidra.de)
*
* This package is free software; you can redistribute it and/or
@ -19,9 +19,9 @@
#pragma once
#include "Hotkey.h"
#include "ScreenConfig.h"
#include "ScreenList.h"
#include "gui/config/IServerConfig.h"
#include "gui/config/ScreenConfig.h"
#include "gui/config/ScreenList.h"
#include <QList>
@ -37,6 +37,8 @@ class MainWindow;
class AppConfig;
class ServerConfig : public ScreenConfig, public synergy::gui::IServerConfig {
using QSettingsProxy = synergy::gui::proxy::QSettingsProxy;
friend class ServerConfigDialog;
friend QTextStream &
operator<<(QTextStream &outStream, const ServerConfig &config);
@ -49,7 +51,15 @@ public:
bool operator==(const ServerConfig &sc) const;
const ScreenList &screens() const { return m_Screens; }
//
// Overrides
//
const ScreenList &screens() const override { return m_Screens; }
bool enableDragAndDrop() const override { return m_EnableDragAndDrop; }
//
// New methods
//
int numColumns() const { return m_Columns; }
int numRows() const { return m_Rows; }
bool hasHeartbeat() const { return m_HasHeartbeat; }
@ -64,23 +74,29 @@ public:
int switchCornerSize() const { return m_SwitchCornerSize; }
const QList<bool> &switchCorners() const { return m_SwitchCorners; }
const HotkeyList &hotkeys() const { return m_Hotkeys; }
bool enableDragAndDrop() const override { return m_EnableDragAndDrop; }
bool disableLockToScreen() const { return m_DisableLockToScreen; }
bool clipboardSharing() const { return m_ClipboardSharing; }
size_t clipboardSharingSize() const { return m_ClipboardSharingSize; }
static size_t defaultClipboardSharingSize();
void commit();
//
// Overrides
//
bool save(const QString &fileName) const override;
bool screenExists(const QString &screenName) const override;
void save(QFile &file) const override;
bool isFull() const override;
//
// New methods
//
void commit();
int numScreens() const;
int autoAddScreen(const QString name);
const QString &getServerName() const;
void updateServerName();
const QString &configFile() const;
bool useExternalConfig() const;
bool isFull() const override;
bool screenExists(const QString &screenName) const override;
void addClient(const QString &clientName);
QString getClientAddress() const;
void setClientAddress(const QString &address);
@ -88,7 +104,7 @@ public:
private:
void recall();
void setupScreens();
QSettings &settings();
QSettingsProxy &settings();
ScreenList &screens() { return m_Screens; }
void setScreens(const ScreenList &screens) { m_Screens = screens; }
void addScreen(const Screen &screen) { m_Screens.append(screen); }

View file

@ -28,6 +28,8 @@
#include <QtCore>
#include <QtGui>
using enum ScreenConfig::SwitchCorner;
ServerConfigDialog::ServerConfigDialog(
QWidget *parent, ServerConfig &config, AppConfig &appConfig)
: QDialog(parent, Qt::WindowTitleHint | Qt::WindowSystemMenuHint),
@ -65,13 +67,13 @@ ServerConfigDialog::ServerConfigDialog(
m_pSpinBoxSwitchDoubleTap->setValue(serverConfig().switchDoubleTap());
m_pCheckBoxCornerTopLeft->setChecked(
serverConfig().switchCorner(ScreenConfig::TopLeft));
serverConfig().switchCorner(static_cast<int>(TopLeft)));
m_pCheckBoxCornerTopRight->setChecked(
serverConfig().switchCorner(ScreenConfig::TopRight));
serverConfig().switchCorner(static_cast<int>(TopRight)));
m_pCheckBoxCornerBottomLeft->setChecked(
serverConfig().switchCorner(ScreenConfig::BottomLeft));
serverConfig().switchCorner(static_cast<int>(BottomLeft)));
m_pCheckBoxCornerBottomRight->setChecked(
serverConfig().switchCorner(ScreenConfig::BottomRight));
serverConfig().switchCorner(static_cast<int>(BottomRight)));
m_pSpinBoxSwitchCornerSize->setValue(serverConfig().switchCornerSize());
m_pCheckBoxDisableLockToScreen->setChecked(
serverConfig().disableLockToScreen());
@ -189,25 +191,25 @@ ServerConfigDialog::ServerConfigDialog(
connect(
m_pCheckBoxCornerTopLeft, &QCheckBox::stateChanged, this,
[this](const int &v) {
serverConfig().setSwitchCorner(ScreenConfig::TopLeft, v);
serverConfig().setSwitchCorner(static_cast<int>(TopLeft), v);
onChange();
});
connect(
m_pCheckBoxCornerTopRight, &QCheckBox::stateChanged, this,
[this](const int &v) {
serverConfig().setSwitchCorner(ScreenConfig::TopRight, v);
serverConfig().setSwitchCorner(static_cast<int>(TopRight), v);
onChange();
});
connect(
m_pCheckBoxCornerBottomLeft, &QCheckBox::stateChanged, this,
[this](const int &v) {
serverConfig().setSwitchCorner(ScreenConfig::BottomLeft, v);
serverConfig().setSwitchCorner(static_cast<int>(BottomLeft), v);
onChange();
});
connect(
m_pCheckBoxCornerBottomRight, &QCheckBox::stateChanged, this,
[this](const int &v) {
serverConfig().setSwitchCorner(ScreenConfig::BottomRight, v);
serverConfig().setSwitchCorner(static_cast<int>(BottomRight), v);
onChange();
});
connect(

View file

@ -40,12 +40,15 @@ using namespace synergy::gui;
const char *const kProProductName = "Synergy 1 Pro";
SettingsDialog::SettingsDialog(
QWidget *parent, AppConfig &config, const License &license)
MainWindow *parent, AppConfig &appConfig, const IServerConfig &serverConfig,
const License &license, const CoreProcess &coreProcess)
: QDialog(parent, Qt::WindowTitleHint | Qt::WindowSystemMenuHint),
Ui::SettingsDialogBase(),
m_appConfig(config),
m_appConfig(appConfig),
m_serverConfig(serverConfig),
m_license(license),
m_tlsUtility(config, license) {
m_coreProcess(coreProcess),
m_tlsUtility(appConfig, license) {
setupUi(this);
@ -53,124 +56,18 @@ SettingsDialog::SettingsDialog(
// the developer was looking at, and it's easy to accidentally save that.
m_pTabWidget->setCurrentIndex(0);
m_pMainWindow = dynamic_cast<MainWindow *>(parent);
loadFromConfig();
m_wasOriginallySystemScope = appConfig().isActiveScopeSystem();
updateControlsEnabled();
const auto &serveConfig = m_pMainWindow->serverConfig();
m_wasOriginallySystemScope = m_appConfig.isActiveScopeSystem();
updateControls();
m_pScreenNameError = new validators::ValidationError(this);
m_pLineEditScreenName->setValidator(new validators::ScreenNameValidator(
m_pLineEditScreenName, m_pScreenNameError, &serveConfig.screens()));
m_pLineEditScreenName, m_pScreenNameError, &serverConfig.screens()));
}
void SettingsDialog::accept() {
if (!m_pLineEditScreenName->hasAcceptableInput()) {
QMessageBox::warning(
this, tr("Invalid screen name"), m_pScreenNameError->message());
return;
}
appConfig().setLoadFromSystemScope(m_pRadioSystemScope->isChecked());
appConfig().setScreenName(m_pLineEditScreenName->text());
appConfig().setPort(m_pSpinBoxPort->value());
appConfig().setNetworkInterface(m_pLineEditInterface->text());
appConfig().setLogLevel(m_pComboLogLevel->currentIndex());
appConfig().setLogToFile(m_pCheckBoxLogToFile->isChecked());
appConfig().setLogFilename(m_pLineEditLogFilename->text());
appConfig().setElevateMode(
static_cast<ElevateMode>(m_pComboElevate->currentIndex()));
appConfig().setAutoHide(m_pCheckBoxAutoHide->isChecked());
appConfig().setPreventSleep(m_pCheckBoxPreventSleep->isChecked());
appConfig().setTlsCertPath(m_pLineEditCertificatePath->text());
appConfig().setTlsKeyLength(m_pComboBoxKeyLength->currentText().toInt());
appConfig().setTlsEnabled(m_pCheckBoxEnableCrypto->isChecked());
appConfig().setLanguageSync(m_pCheckBoxLanguageSync->isChecked());
appConfig().setInvertScrollDirection(m_pCheckBoxScrollDirection->isChecked());
appConfig().setEnableService(m_pCheckBoxServiceEnabled->isChecked());
appConfig().setCloseToTray(m_pCheckBoxCloseToTray->isChecked());
appConfig().setInvertConnection(m_pInvertConnection->isChecked());
QDialog::accept();
}
void SettingsDialog::reject() {
// restore original system scope value on reject.
if (appConfig().isActiveScopeSystem() != m_wasOriginallySystemScope) {
appConfig().setLoadFromSystemScope(m_wasOriginallySystemScope);
}
QDialog::reject();
}
void SettingsDialog::loadFromConfig() {
m_pLineEditScreenName->setText(appConfig().screenName());
m_pSpinBoxPort->setValue(appConfig().port());
m_pLineEditInterface->setText(appConfig().networkInterface());
m_pComboLogLevel->setCurrentIndex(appConfig().logLevel());
m_pCheckBoxLogToFile->setChecked(appConfig().logToFile());
m_pLineEditLogFilename->setText(appConfig().logFilename());
m_pCheckBoxAutoHide->setChecked(appConfig().autoHide());
m_pCheckBoxPreventSleep->setChecked(appConfig().preventSleep());
m_pLineEditCertificatePath->setText(appConfig().tlsCertPath());
m_pCheckBoxEnableCrypto->setChecked(m_appConfig.tlsEnabled());
m_pCheckBoxLanguageSync->setChecked(m_appConfig.languageSync());
m_pCheckBoxScrollDirection->setChecked(m_appConfig.invertScrollDirection());
m_pCheckBoxServiceEnabled->setChecked(m_appConfig.enableService());
m_pCheckBoxCloseToTray->setChecked(m_appConfig.closeToTray());
if (m_appConfig.isActiveScopeSystem()) {
m_pRadioSystemScope->setChecked(true);
} else {
m_pRadioUserScope->setChecked(true);
}
m_pInvertConnection->setChecked(m_appConfig.invertConnection());
m_pInvertConnection->setEnabled(
m_license.productEdition() == Edition::kBusiness);
updateTlsControls();
}
void SettingsDialog::updateTlsControls() {
if (QFile(appConfig().tlsCertPath()).exists()) {
updateKeyLengthOnFile(appConfig().tlsCertPath());
} else {
const auto keyLengthText = QString::number(appConfig().tlsKeyLength());
m_pComboBoxKeyLength->setCurrentIndex(
m_pComboBoxKeyLength->findText(keyLengthText));
}
m_pCheckBoxEnableCrypto->setChecked(m_appConfig.tlsEnabled());
updateTlsControlsEnabled();
}
void SettingsDialog::updateTlsControlsEnabled() {
bool writable = appConfig().isActiveScopeWritable();
auto clientMode = appConfig().clientGroupChecked();
auto tlsAvailable = m_tlsUtility.isAvailableAndEnabled();
auto tlsChecked = m_pCheckBoxEnableCrypto->isChecked();
auto enabled = writable && !clientMode && tlsAvailable && tlsChecked;
qDebug(
"tls enabled=%d, writable=%d, client=%d, available=%d, checked=%d",
enabled, writable, clientMode, tlsAvailable, tlsChecked);
m_pLabelKeyLength->setEnabled(enabled);
m_pComboBoxKeyLength->setEnabled(enabled);
m_pLabelCertificate->setEnabled(enabled);
m_pLineEditCertificatePath->setEnabled(enabled);
m_pPushButtonBrowseCert->setEnabled(enabled);
m_pPushButtonRegenCert->setEnabled(enabled);
}
bool SettingsDialog::isClientMode() const {
return m_pMainWindow->coreMode() == MainWindow::CoreMode::Client;
}
//
// Auto-connect slots
//
void SettingsDialog::on_m_pCheckBoxLogToFile_stateChanged(int i) {
bool checked = i == 2;
@ -206,9 +103,9 @@ void SettingsDialog::on_m_pCheckBoxEnableCrypto_clicked(bool) {
void SettingsDialog::on_m_pRadioSystemScope_toggled(bool checked) {
// We only need to test the System scoped Radio as they are connected
appConfig().setLoadFromSystemScope(checked);
m_appConfig.setLoadFromSystemScope(checked);
loadFromConfig();
updateControlsEnabled();
updateControls();
}
void SettingsDialog::on_m_pPushButtonBrowseCert_clicked() {
@ -233,18 +130,6 @@ void SettingsDialog::on_m_pComboBoxKeyLength_currentIndexChanged(int index) {
updateTlsRegenerateButton();
}
void SettingsDialog::updateTlsRegenerateButton() {
const auto keyLength = m_pComboBoxKeyLength->currentText().toInt();
auto keyChanged = appConfig().tlsKeyLength() != keyLength;
auto pathChanged =
appConfig().tlsCertPath() != m_pLineEditCertificatePath->text();
// NOR the above bools, if any have changed regen should be disabled as it
// will be done on save
auto nor = !(keyChanged || pathChanged);
m_pPushButtonRegenCert->setEnabled(
nor && m_pCheckBoxEnableCrypto->isChecked());
}
void SettingsDialog::on_m_pPushButtonRegenCert_clicked() {
if (m_tlsUtility.generateCertificate()) {
QMessageBox::information(
@ -253,6 +138,134 @@ void SettingsDialog::on_m_pPushButtonRegenCert_clicked() {
}
}
void SettingsDialog::on_m_pCheckBoxServiceEnabled_toggled(bool) {
updateControls();
}
//
// End of auto-connect slots
//
void SettingsDialog::accept() {
if (!m_pLineEditScreenName->hasAcceptableInput()) {
QMessageBox::warning(
this, tr("Invalid screen name"), m_pScreenNameError->message());
return;
}
m_appConfig.setLoadFromSystemScope(m_pRadioSystemScope->isChecked());
m_appConfig.setScreenName(m_pLineEditScreenName->text());
m_appConfig.setPort(m_pSpinBoxPort->value());
m_appConfig.setNetworkInterface(m_pLineEditInterface->text());
m_appConfig.setLogLevel(m_pComboLogLevel->currentIndex());
m_appConfig.setLogToFile(m_pCheckBoxLogToFile->isChecked());
m_appConfig.setLogFilename(m_pLineEditLogFilename->text());
m_appConfig.setElevateMode(
static_cast<ElevateMode>(m_pComboElevate->currentIndex()));
m_appConfig.setAutoHide(m_pCheckBoxAutoHide->isChecked());
m_appConfig.setPreventSleep(m_pCheckBoxPreventSleep->isChecked());
m_appConfig.setTlsCertPath(m_pLineEditCertificatePath->text());
m_appConfig.setTlsKeyLength(m_pComboBoxKeyLength->currentText().toInt());
m_appConfig.setTlsEnabled(m_pCheckBoxEnableCrypto->isChecked());
m_appConfig.setLanguageSync(m_pCheckBoxLanguageSync->isChecked());
m_appConfig.setInvertScrollDirection(m_pCheckBoxScrollDirection->isChecked());
m_appConfig.setEnableService(m_pCheckBoxServiceEnabled->isChecked());
m_appConfig.setCloseToTray(m_pCheckBoxCloseToTray->isChecked());
m_appConfig.setInvertConnection(m_pInvertConnection->isChecked());
QDialog::accept();
}
void SettingsDialog::reject() {
// restore original system scope value on reject.
if (m_appConfig.isActiveScopeSystem() != m_wasOriginallySystemScope) {
m_appConfig.setLoadFromSystemScope(m_wasOriginallySystemScope);
}
QDialog::reject();
}
void SettingsDialog::loadFromConfig() {
m_pLineEditScreenName->setText(m_appConfig.screenName());
m_pSpinBoxPort->setValue(m_appConfig.port());
m_pLineEditInterface->setText(m_appConfig.networkInterface());
m_pComboLogLevel->setCurrentIndex(m_appConfig.logLevel());
m_pCheckBoxLogToFile->setChecked(m_appConfig.logToFile());
m_pLineEditLogFilename->setText(m_appConfig.logFilename());
m_pCheckBoxAutoHide->setChecked(m_appConfig.autoHide());
m_pCheckBoxPreventSleep->setChecked(m_appConfig.preventSleep());
m_pLineEditCertificatePath->setText(m_appConfig.tlsCertPath());
m_pCheckBoxEnableCrypto->setChecked(m_appConfig.tlsEnabled());
m_pCheckBoxLanguageSync->setChecked(m_appConfig.languageSync());
m_pCheckBoxScrollDirection->setChecked(m_appConfig.invertScrollDirection());
m_pCheckBoxServiceEnabled->setChecked(m_appConfig.enableService());
m_pCheckBoxCloseToTray->setChecked(m_appConfig.closeToTray());
m_pComboElevate->setCurrentIndex(static_cast<int>(m_appConfig.elevateMode()));
if (m_appConfig.isActiveScopeSystem()) {
m_pRadioSystemScope->setChecked(true);
} else {
m_pRadioUserScope->setChecked(true);
}
m_pInvertConnection->setChecked(m_appConfig.invertConnection());
m_pInvertConnection->setEnabled(
m_license.productEdition() == Edition::kBusiness);
updateTlsControls();
}
void SettingsDialog::updateTlsControls() {
if (QFile(m_appConfig.tlsCertPath()).exists()) {
updateKeyLengthOnFile(m_appConfig.tlsCertPath());
} else {
const auto keyLengthText = QString::number(m_appConfig.tlsKeyLength());
m_pComboBoxKeyLength->setCurrentIndex(
m_pComboBoxKeyLength->findText(keyLengthText));
}
m_pCheckBoxEnableCrypto->setChecked(m_appConfig.tlsEnabled());
updateTlsControlsEnabled();
}
void SettingsDialog::updateTlsControlsEnabled() {
const auto writable = m_appConfig.isActiveScopeWritable();
const auto clientMode = m_appConfig.clientGroupChecked();
const auto tlsAvailable = m_tlsUtility.isAvailableAndEnabled();
const auto tlsChecked = m_pCheckBoxEnableCrypto->isChecked();
auto enabled = writable && !clientMode && tlsAvailable && tlsChecked;
qDebug(
"tls enabled=%d, writable=%d, client=%d, available=%d, checked=%d",
enabled, writable, clientMode, tlsAvailable, tlsChecked);
m_pLabelKeyLength->setEnabled(enabled);
m_pComboBoxKeyLength->setEnabled(enabled);
m_pLabelCertificate->setEnabled(enabled);
m_pLineEditCertificatePath->setEnabled(enabled);
m_pPushButtonBrowseCert->setEnabled(enabled);
m_pPushButtonRegenCert->setEnabled(enabled);
}
bool SettingsDialog::isClientMode() const {
return m_coreProcess.mode() == MainWindow::CoreMode::Client;
}
void SettingsDialog::updateTlsRegenerateButton() {
const auto writable = m_appConfig.isActiveScopeWritable();
const auto keyLength = m_pComboBoxKeyLength->currentText().toInt();
const auto path = m_pLineEditCertificatePath->text();
const auto keyChanged = m_appConfig.tlsKeyLength() != keyLength;
const auto pathChanged = m_appConfig.tlsCertPath() != path;
const auto tlsEnabled = m_pCheckBoxEnableCrypto->isChecked();
m_pPushButtonRegenCert->setEnabled(
writable && tlsEnabled && (keyChanged || pathChanged));
}
void SettingsDialog::updateKeyLengthOnFile(const QString &path) {
TlsCertificate ssl;
if (!QFile(path).exists()) {
@ -262,52 +275,48 @@ void SettingsDialog::updateKeyLengthOnFile(const QString &path) {
auto length = ssl.getCertKeyLength(path);
auto index = m_pComboBoxKeyLength->findText(QString::number(length));
m_pComboBoxKeyLength->setCurrentIndex(index);
appConfig().setTlsKeyLength(length);
m_appConfig.setTlsKeyLength(length);
}
void SettingsDialog::updateControlsEnabled() {
bool writable = appConfig().isActiveScopeWritable();
void SettingsDialog::updateControls() {
#if defined(Q_OS_WIN)
const auto serviceAvailable = true;
#else
// service not supported on unix yet, so always disable.
const auto serviceAvailable = false;
m_pGroupService->setTitle("Service (Windows only)");
#endif
const bool writable = m_appConfig.isActiveScopeWritable();
const bool serviceChecked = m_pCheckBoxServiceEnabled->isChecked();
const bool logToFile = m_pCheckBoxLogToFile->isChecked();
m_pLineEditScreenName->setEnabled(writable);
m_pSpinBoxPort->setEnabled(writable);
m_pLineEditInterface->setEnabled(writable);
m_pComboLogLevel->setEnabled(writable);
m_pCheckBoxLogToFile->setEnabled(writable);
m_pComboElevate->setEnabled(writable);
m_pCheckBoxAutoHide->setEnabled(writable);
m_pCheckBoxPreventSleep->setEnabled(writable);
m_pLineEditCertificatePath->setEnabled(writable);
m_pComboBoxKeyLength->setEnabled(writable);
m_pPushButtonBrowseCert->setEnabled(writable);
m_pCheckBoxEnableCrypto->setEnabled(writable);
m_pCheckBoxServiceEnabled->setEnabled(writable);
m_pCheckBoxCloseToTray->setEnabled(writable);
m_pCheckBoxServiceEnabled->setEnabled(writable && serviceAvailable);
m_pLabelElevate->setEnabled(writable && serviceChecked && serviceAvailable);
m_pComboElevate->setEnabled(writable && serviceChecked && serviceAvailable);
m_pCheckBoxLanguageSync->setEnabled(writable && isClientMode());
m_pCheckBoxScrollDirection->setEnabled(writable && isClientMode());
#if !defined(Q_OS_WIN)
m_pCheckBoxServiceEnabled->setEnabled(false);
#endif
m_pLabelLogPath->setEnabled(writable && m_pCheckBoxLogToFile->isChecked());
m_pLineEditLogFilename->setEnabled(
writable && m_pCheckBoxLogToFile->isChecked());
m_pButtonBrowseLog->setEnabled(writable && m_pCheckBoxLogToFile->isChecked());
m_pLabelLogPath->setEnabled(writable && logToFile);
m_pLineEditLogFilename->setEnabled(writable && logToFile);
m_pButtonBrowseLog->setEnabled(writable && logToFile);
updateTlsControlsEnabled();
if (writable) {
updateTlsRegenerateButton();
}
#if defined(Q_OS_WIN)
m_pComboElevate->setCurrentIndex(static_cast<int>(appConfig().elevateMode()));
#else
// elevate checkbox is only usable on ms windows.
m_pLabelElevate->setEnabled(false);
m_pComboElevate->setEnabled(false);
#endif
updateTlsControls();
}

View file

@ -20,6 +20,9 @@
#include "ui_SettingsDialogBase.h"
#include "gui/config/AppConfig.h"
#include "gui/config/IServerConfig.h"
#include "gui/core/CoreProcess.h"
#include "gui/core/CoreTool.h"
#include "gui/tls/TlsUtility.h"
#include "license/License.h"
@ -28,15 +31,19 @@
#include <QDialog>
class MainWindow;
class AppConfig;
class SettingsDialog : public QDialog, public Ui::SettingsDialogBase {
using IServerConfig = synergy::gui::IServerConfig;
using CoreProcess = synergy::gui::CoreProcess;
using License = synergy::license::License;
Q_OBJECT
public:
SettingsDialog(
QWidget *parent, AppConfig &config,
const synergy::license::License &license);
MainWindow *parent, AppConfig &appConfig,
const IServerConfig &serverConfig, const License &license,
const CoreProcess &coreProcess);
static QString browseForSynergyc(
QWidget *parent, const QString &programDir,
const QString &coreClientName);
@ -44,10 +51,19 @@ public:
QWidget *parent, const QString &programDir,
const QString &coreServerName);
protected:
private slots:
void on_m_pCheckBoxEnableCrypto_clicked(bool checked);
void on_m_pCheckBoxLogToFile_stateChanged(int);
void on_m_pButtonBrowseLog_clicked();
void on_m_pRadioSystemScope_toggled(bool checked);
void on_m_pPushButtonBrowseCert_clicked();
void on_m_pComboBoxKeyLength_currentIndexChanged(int index);
void on_m_pPushButtonRegenCert_clicked();
void on_m_pCheckBoxServiceEnabled_toggled(bool checked);
private:
void accept() override;
void reject() override;
AppConfig &appConfig() { return m_appConfig; }
/// @brief Load all settings.
void loadFromConfig();
@ -59,31 +75,23 @@ protected:
void updateKeyLengthOnFile(const QString &path);
/// @brief Enables controls when they should be.
void updateControlsEnabled();
void updateControls();
bool isClientMode() const;
void updateTlsControls();
void updateTlsControlsEnabled();
private:
MainWindow *m_pMainWindow;
AppConfig &m_appConfig;
[[no_unique_address]] CoreTool m_coreTool;
const synergy::license::License &m_license;
synergy::gui::TlsUtility m_tlsUtility;
validators::ValidationError *m_pScreenNameError;
/// @brief Stores settings scope at start of settings dialog
/// This is neccessary to restore state if user changes
/// This is necessary to restore state if user changes
/// the scope and doesn't save changes
bool m_wasOriginallySystemScope = false;
private slots:
void on_m_pCheckBoxEnableCrypto_clicked(bool checked);
void on_m_pCheckBoxLogToFile_stateChanged(int);
void on_m_pButtonBrowseLog_clicked();
void on_m_pRadioSystemScope_toggled(bool checked);
void on_m_pPushButtonBrowseCert_clicked();
void on_m_pComboBoxKeyLength_currentIndexChanged(int index);
void on_m_pPushButtonRegenCert_clicked();
AppConfig &m_appConfig;
const IServerConfig &m_serverConfig;
const License &m_license;
const CoreProcess &m_coreProcess;
synergy::gui::TlsUtility m_tlsUtility;
};

View file

@ -1,7 +1,6 @@
/*
* synergy -- mouse and keyboard sharing utility
* Copyright (C) 2012-2021 Symless Ltd.
* Copyright (C) 2008 Volker Lanz (vl@fidra.de)
* Copyright (C) 2021 Symless Ltd.
*
* This package is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
@ -15,6 +14,7 @@
* 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 "ScreenDuplicationsValidator.h"
namespace validators {

View file

@ -1,7 +1,6 @@
/*
* synergy -- mouse and keyboard sharing utility
* Copyright (C) 2012-2021 Symless Ltd.
* Copyright (C) 2008 Volker Lanz (vl@fidra.de)
* Copyright (C) 2021 Symless Ltd.
*
* This package is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
@ -15,11 +14,12 @@
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef SCREENDUPLICATIONSVALIDATOR_H
#define SCREENDUPLICATIONSVALIDATOR_H
#pragma once
#include "IStringValidator.h"
#include "ScreenList.h"
#include "gui/config/ScreenList.h"
namespace validators {
@ -35,5 +35,3 @@ public:
};
} // namespace validators
#endif // SCREENDUPLICATIONSVALIDATOR_H

View file

@ -1,7 +1,6 @@
/*
* synergy -- mouse and keyboard sharing utility
* Copyright (C) 2012-2021 Symless Ltd.
* Copyright (C) 2008 Volker Lanz (vl@fidra.de)
* Copyright (C) 2021 Symless Ltd.
*
* This package is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License

View file

@ -18,7 +18,7 @@
#pragma once
#include "LineEditValidator.h"
#include "ScreenList.h"
#include "gui/config/ScreenList.h"
#include "validators/ValidationError.h"
namespace validators {

View file

@ -520,7 +520,7 @@ bool AppConfig::useInternalConfig() const { return m_UseInternalConfig; }
bool AppConfig::clientGroupChecked() const { return m_ClientGroupChecked; }
QString AppConfig::serverHostname() const { return m_ServerHostname; }
const QString &AppConfig::serverHostname() const { return m_ServerHostname; }
void AppConfig::setActivationHasRun(bool value) { m_ActivationHasRun = value; }

View file

@ -136,10 +136,9 @@ public:
*/
void commit();
/**
* Getters
*/
//
// Getters (overrides)
//
ProcessMode processMode() const override;
ElevateMode elevateMode() const override;
bool tlsEnabled() const override;
@ -161,6 +160,12 @@ public:
bool useExternalConfig() const override;
const QString &configFile() const override;
const QString &networkInterface() const override;
const QString &serverHostname() const override;
//
// Getters (new methods)
//
bool isActiveScopeWritable() const;
bool isActiveScopeSystem() const;
int logLevel() const;
@ -172,7 +177,6 @@ public:
bool serverGroupChecked() const;
bool useInternalConfig() const;
bool clientGroupChecked() const;
QString serverHostname() const;
QString lastVersion() const;
bool enableService() const;
bool closeToTray() const;
@ -182,9 +186,9 @@ public:
bool showDevThanks() const;
bool showCloseReminder() const;
/**
* Setters
*/
//
// Setters
//
void setActivationHasRun(bool value);
void setScreenName(const QString &s);

View file

@ -30,6 +30,8 @@ const auto kUnixSystemConfigPath = "/usr/local/etc/symless/";
namespace synergy::gui {
using namespace proxy;
QString getSystemSettingPath() {
const QString settingFilename(kSystemConfigFilename);
#if defined(Q_OS_WIN)
@ -92,6 +94,7 @@ ConfigScopes::ConfigScopes() {
// if we set the scope specifically then we also have to set the application
// name and the organisation name which breaks backwards compatibility.
m_pUserSettings = std::make_unique<QSettings>();
m_userSettingsProxy.set(*m_pUserSettings);
qDebug() << "user settings path:" << m_pUserSettings->fileName();
@ -104,6 +107,7 @@ ConfigScopes::ConfigScopes() {
m_pSystemSettings = std::make_unique<QSettings>(
QSettings::Format::IniFormat, QSettings::Scope::SystemScope, orgName,
appName);
m_systemSettingsProxy.set(*m_pSystemSettings);
qDebug() << "system settings path:" << m_pSystemSettings->fileName();
@ -124,7 +128,7 @@ void ConfigScopes::save() {
}
bool ConfigScopes::isActiveScopeWritable() const {
return activeSettings()->isWritable();
return activeSettings().isWritable();
}
void ConfigScopes::setActiveScope(ConfigScopes::Scope scope) {
@ -140,15 +144,23 @@ bool ConfigScopes::scopeContains(const QString &name, Scope scope) const {
case Scope::System:
return m_pSystemSettings->contains(name);
default:
return activeSettings()->contains(name);
return activeSettings().contains(name);
}
}
QSettings *ConfigScopes::activeSettings() const {
QSettingsProxy &ConfigScopes::activeSettings() {
if (m_currentScope == Scope::User) {
return m_pUserSettings.get();
return m_userSettingsProxy;
} else {
return m_pSystemSettings.get();
return m_systemSettingsProxy;
}
}
const QSettingsProxy &ConfigScopes::activeSettings() const {
if (m_currentScope == Scope::User) {
return m_userSettingsProxy;
} else {
return m_systemSettingsProxy;
}
}
@ -160,7 +172,7 @@ QVariant ConfigScopes::getFromScope(
case Scope::System:
return m_pSystemSettings->value(name, defaultValue);
default:
return activeSettings()->value(name, defaultValue);
return activeSettings().value(name, defaultValue);
}
}
@ -174,7 +186,7 @@ void ConfigScopes::setInScope(
m_pSystemSettings->setValue(name, value);
break;
default:
activeSettings()->setValue(name, value);
activeSettings().setValue(name, value);
break;
}
}

View file

@ -28,6 +28,8 @@ namespace synergy::gui {
/// @brief Encapsulates Qt config for both user and global scopes.
class ConfigScopes : public QObject, public IConfigScopes {
using QSettingsProxy = synergy::gui::proxy::QSettingsProxy;
Q_OBJECT
public:
@ -47,7 +49,8 @@ public:
Scope scope = Scope::Current) const override;
void setActiveScope(Scope scope = Scope::User) override;
Scope activeScope() const override;
QSettings *activeSettings() const override;
QSettingsProxy &activeSettings() override;
const QSettingsProxy &activeSettings() const override;
signals:
void ready();
@ -57,6 +60,8 @@ private:
Scope m_currentScope = Scope::User;
std::unique_ptr<QSettings> m_pUserSettings;
std::unique_ptr<QSettings> m_pSystemSettings;
QSettingsProxy m_userSettingsProxy;
QSettingsProxy m_systemSettingsProxy;
};
} // namespace synergy::gui

View file

@ -49,6 +49,7 @@ public:
virtual bool useExternalConfig() const = 0;
virtual const QString &configFile() const = 0;
virtual const QString &networkInterface() const = 0;
virtual const QString &serverHostname() const = 0;
};
} // namespace synergy::gui

View file

@ -17,6 +17,8 @@
#pragma once
#include "gui/proxy/QSettingsProxy.h"
#include <QSettings>
#include <QString>
#include <QVariant>
@ -24,6 +26,8 @@
namespace synergy::gui {
class IConfigScopes {
using QSettingsProxy = synergy::gui::proxy::QSettingsProxy;
public:
enum class Scope { Current, System, User };
@ -32,7 +36,8 @@ public:
virtual Scope activeScope() const = 0;
virtual void setActiveScope(Scope scope = Scope::User) = 0;
virtual bool isActiveScopeWritable() const = 0;
virtual QSettings *activeSettings() const = 0;
virtual QSettingsProxy &activeSettings() = 0;
virtual const QSettingsProxy &activeSettings() const = 0;
/**
* @brief Signals to listeners that the settings that they should read.
@ -40,7 +45,7 @@ public:
virtual void signalReady() = 0;
/**
* @brief Signalls to listeners to save and calls `sync` on underlying Qt
* @brief Signals to listeners to save and calls `sync` on underlying Qt
* config.
*
*/

View file

@ -20,6 +20,8 @@
#include <QFile>
#include <QString>
#include "ScreenList.h"
namespace synergy::gui {
class IServerConfig {
@ -30,6 +32,7 @@ public:
virtual bool save(const QString &fileName) const = 0;
virtual void save(QFile &file) const = 0;
virtual bool enableDragAndDrop() const = 0;
virtual const ScreenList &screens() const = 0;
};
} // namespace synergy::gui

View file

@ -1,6 +1,6 @@
/*
* synergy -- mouse and keyboard sharing utility
* Copyright (C) 2012-2016 Symless Ltd.
* Copyright (C) 2012 Symless Ltd.
* Copyright (C) 2008 Volker Lanz (vl@fidra.de)
*
* This package is free software; you can redistribute it and/or
@ -17,19 +17,19 @@
*/
#include "Screen.h"
#include "config/ScreenConfig.h"
#include <QtCore>
#include <QtGui>
Screen::Screen()
: m_Pixmap(QPixmap(":res/icons/64x64/video-display.png")),
m_Swapped(false) {
init();
}
using namespace synergy::gui::proxy;
using enum ScreenConfig::Modifier;
using enum ScreenConfig::SwitchCorner;
using enum ScreenConfig::Fix;
Screen::Screen(const QString &name)
: m_Pixmap(QPixmap(":res/icons/64x64/video-display.png")),
m_Swapped(false) {
Screen::Screen() { init(); }
Screen::Screen(const QString &name) {
init();
setName(name);
}
@ -45,17 +45,17 @@ void Screen::init() {
// m_Modifiers, m_SwitchCorners and m_Fixes are QLists we use like fixed-size
// arrays, thus we need to make sure to fill them with the required number of
// elements.
for (int i = 0; i < NumModifiers; i++)
for (int i = 0; i < static_cast<int>(NumModifiers); i++)
modifiers() << i;
for (int i = 0; i < NumSwitchCorners; i++)
for (int i = 0; i < static_cast<int>(NumSwitchCorners); i++)
switchCorners() << false;
for (int i = 0; i < NumFixes; i++)
for (int i = 0; i < static_cast<int>(NumFixes); i++)
fixes() << false;
}
void Screen::loadSettings(QSettings &settings) {
void Screen::loadSettings(QSettingsProxy &settings) {
setName(settings.value("name").toString());
if (name().isEmpty())
@ -66,13 +66,14 @@ void Screen::loadSettings(QSettings &settings) {
readSettings(settings, aliases(), "alias", QString(""));
readSettings(
settings, modifiers(), "modifier", static_cast<int>(DefaultMod),
NumModifiers);
static_cast<int>(NumModifiers));
readSettings(
settings, switchCorners(), "switchCorner", false, NumSwitchCorners);
readSettings(settings, fixes(), "fix", false, NumFixes);
settings, switchCorners(), "switchCorner", 0,
static_cast<int>(NumSwitchCorners));
readSettings(settings, fixes(), "fix", 0, static_cast<int>(NumFixes));
}
void Screen::saveSettings(QSettings &settings) const {
void Screen::saveSettings(QSettingsProxy &settings) const {
settings.setValue("name", name());
if (name().isEmpty())
@ -130,16 +131,3 @@ bool Screen::operator==(const Screen &screen) const {
m_Fixes == screen.m_Fixes && m_Swapped == screen.m_Swapped &&
m_isServer == screen.m_isServer;
}
QDataStream &operator<<(QDataStream &outStream, const Screen &screen) {
return outStream << screen.name() << screen.switchCornerSize()
<< screen.aliases() << screen.modifiers()
<< screen.switchCorners() << screen.fixes()
<< screen.isServer();
}
QDataStream &operator>>(QDataStream &inStream, Screen &screen) {
return inStream >> screen.m_Name >> screen.m_SwitchCornerSize >>
screen.m_Aliases >> screen.m_Modifiers >> screen.m_SwitchCorners >>
screen.m_Fixes >> screen.m_isServer;
}

View file

@ -1,6 +1,6 @@
/*
* synergy -- mouse and keyboard sharing utility
* Copyright (C) 2012-2016 Symless Ltd.
* Copyright (C) 2012 Symless Ltd.
* Copyright (C) 2008 Volker Lanz (vl@fidra.de)
*
* This package is free software; you can redistribute it and/or
@ -20,6 +20,8 @@
#include "ScreenConfig.h"
#include "gui/proxy/QSettingsProxy.h"
#include <QList>
#include <QPixmap>
#include <QString>
@ -30,34 +32,49 @@ class QTextStream;
class ScreenSettingsDialog;
class Screen : public ScreenConfig {
friend QDataStream &operator<<(QDataStream &outStream, const Screen &screen);
friend QDataStream &operator>>(QDataStream &inStream, Screen &screen);
using QSettingsProxy = synergy::gui::proxy::QSettingsProxy;
friend class ScreenSettingsDialog;
friend class ScreenSetupModel;
friend class ScreenSetupView;
public:
Screen();
Screen(const QString &name);
friend QDataStream &operator<<(QDataStream &outStream, const Screen &screen) {
return outStream << screen.name() << screen.switchCornerSize()
<< screen.aliases() << screen.modifiers()
<< screen.switchCorners() << screen.fixes()
<< screen.isServer();
}
friend QDataStream &operator>>(QDataStream &inStream, Screen &screen) {
return inStream >> screen.m_Name >> screen.m_SwitchCornerSize >>
screen.m_Aliases >> screen.m_Modifiers >> screen.m_SwitchCorners >>
screen.m_Fixes >> screen.m_isServer;
}
public:
explicit Screen();
explicit Screen(const QString &name);
const QPixmap &pixmap() const { return m_Pixmap; }
const QString &name() const { return m_Name; }
const QStringList &aliases() const { return m_Aliases; }
bool isNull() const { return m_Name.isEmpty(); }
int modifier(int m) const {
return m_Modifiers[m] == DefaultMod ? m : m_Modifiers[m];
return m_Modifiers[m] ==
static_cast<int>(ScreenConfig::Modifier::DefaultMod)
? m
: m_Modifiers[m];
}
const QList<int> &modifiers() const { return m_Modifiers; }
bool switchCorner(int c) const { return m_SwitchCorners[c]; }
const QList<bool> &switchCorners() const { return m_SwitchCorners; }
int switchCornerSize() const { return m_SwitchCornerSize; }
bool fix(Fix f) const { return m_Fixes[f]; }
bool fix(Fix f) const { return m_Fixes[static_cast<int>(f)]; }
const QList<bool> &fixes() const { return m_Fixes; }
void loadSettings(QSettings &settings);
void saveSettings(QSettings &settings) const;
void loadSettings(QSettingsProxy &settings);
void saveSettings(QSettingsProxy &settings) const;
QTextStream &writeScreensSection(QTextStream &outStream) const;
QTextStream &writeAliasesSection(QTextStream &outStream) const;
@ -84,18 +101,13 @@ protected:
void setSwapped(bool on) { m_Swapped = on; }
private:
QPixmap m_Pixmap;
QPixmap m_Pixmap = QPixmap(":res/icons/64x64/video-display.png");
QString m_Name;
QStringList m_Aliases;
QList<int> m_Modifiers;
QList<bool> m_SwitchCorners;
int m_SwitchCornerSize;
QList<bool> m_Fixes;
bool m_Swapped;
bool m_Swapped = false;
bool m_isServer = false;
};
QDataStream &operator<<(QDataStream &outStream, const Screen &screen);
QDataStream &operator>>(QDataStream &inStream, Screen &screen);

View file

@ -1,6 +1,6 @@
/*
* synergy -- mouse and keyboard sharing utility
* Copyright (C) 2012-2016 Symless Ltd.
* Copyright (C) 2012 Symless Ltd.
* Copyright (C) 2008 Volker Lanz (vl@fidra.de)
*
* This package is free software; you can redistribute it and/or

View file

@ -18,14 +18,17 @@
#pragma once
#include <QSettings>
#include <QString>
#include <QVariant>
#include "gui/proxy/QSettingsProxy.h"
/// @brief Screen configuration base class
class ScreenConfig {
using QSettingsProxy = synergy::gui::proxy::QSettingsProxy;
public:
enum Modifier {
enum class Modifier {
DefaultMod = -1,
Shift,
Ctrl,
@ -35,29 +38,28 @@ public:
None,
NumModifiers
};
enum SwitchCorner {
enum class SwitchCorner {
TopLeft,
TopRight,
BottomLeft,
BottomRight,
NumSwitchCorners
};
enum Fix { CapsLock, NumLock, ScrollLock, XTest, NumFixes };
enum class Fix { CapsLock, NumLock, ScrollLock, XTest, NumFixes };
protected:
explicit ScreenConfig() = default;
~ScreenConfig() = default;
protected:
template <typename T1, typename T2>
void readSettings(
QSettings &settings, T1 &array, const QString &arrayName,
const T2 &deflt) {
QSettingsProxy &settings, T1 &array, const QString &arrayName,
const T2 &defaultValue) {
int entries = settings.beginReadArray(arrayName + "Array");
array.clear();
for (int i = 0; i < entries; i++) {
settings.setArrayIndex(i);
QVariant v = settings.value(arrayName, deflt);
QVariant v = settings.value(arrayName, defaultValue);
array.append(v.value<T2>());
}
settings.endArray();
@ -65,13 +67,13 @@ protected:
template <typename T1, typename T2>
void readSettings(
QSettings &settings, T1 &array, const QString &arrayName, const T2 &deflt,
int entries) {
QSettingsProxy &settings, T1 &array, const QString &arrayName,
const T2 &defaultValue, int entries) {
Q_ASSERT(array.size() >= entries);
settings.beginReadArray(arrayName + "Array");
for (int i = 0; i < entries; i++) {
settings.setArrayIndex(i);
QVariant v = settings.value(arrayName, deflt);
QVariant v = settings.value(arrayName, defaultValue);
array[i] = v.value<T2>();
}
settings.endArray();
@ -79,7 +81,8 @@ protected:
template <typename T>
void writeSettings(
QSettings &settings, const T &array, const QString &arrayName) const {
QSettingsProxy &settings, const T &array,
const QString &arrayName) const {
settings.beginWriteArray(arrayName + "Array");
for (int i = 0; i < array.size(); i++) {
settings.setArrayIndex(i);

View file

@ -90,7 +90,8 @@ ScreenList::ScreenList(int width) : QList<Screen>(), m_width(width) {}
void ScreenList::addScreenByPriority(const Screen &newScreen) {
int serverIndex = getServerIndex(*this);
auto indexes = getNeighborsIndexes(serverIndex, m_width, size());
auto indexes =
getNeighborsIndexes(serverIndex, m_width, static_cast<int>(size()));
bool isAdded = false;
for (const auto &index : indexes) {

View file

@ -1,7 +1,6 @@
/*
* synergy -- mouse and keyboard sharing utility
* Copyright (C) 2012-2021 Symless Ltd.
* Copyright (C) 2008 Volker Lanz (vl@fidra.de)
* Copyright (C) 2021 Symless Ltd.
*
* This package is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
@ -15,8 +14,8 @@
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef SCREENLIST_H
#define SCREENLIST_H
#pragma once
#include "Screen.h"
@ -51,5 +50,3 @@ public:
*/
bool operator==(const ScreenList &sc) const;
};
#endif // SCREENLIST_H

View file

@ -17,71 +17,67 @@
#include "ClientConnection.h"
#include "messages.h"
#include <QHostAddress>
#include <QMessageBox>
namespace synergy::gui {
ClientConnection::ClientConnection(QWidget &parent, AppConfig &appConfig)
: m_parent(parent),
m_appConfig(appConfig) {}
//
// ClientConnection::Deps
//
void ClientConnection::update(const QString &line) {
if (m_checkConnection && checkMainWindow()) {
if (line.contains("failed to connect to server")) {
m_checkConnection = false;
if (!line.contains("server refused client with our name") &&
!line.contains("Trying next address")) {
showMessage(getMessage(line));
void ClientConnection::Deps::showError(
QWidget *parent, messages::ClientError error,
const QString &address) const {
messages::showClientConnectError(parent, error, address);
}
} else if (line.contains("connected to server")) {
m_checkConnection = false;
//
// ClientConnection
//
void ClientConnection::handleLogLine(const QString &logLine) {
if (!m_showMessage) {
qDebug("message already shown, skipping");
return;
}
if (logLine.contains("failed to connect to server")) {
m_showMessage = false;
// ignore the message if it's about the server refusing by name as
// this will trigger the server to show an 'add client' dialog.
if (logLine.contains("server refused client with our name")) {
qDebug("ignoring client name refused message");
return;
}
showMessage(logLine);
} else if (logLine.contains("connected to server")) {
m_showMessage = false;
}
}
bool ClientConnection::checkMainWindow() {
bool result = m_parent.isActiveWindow();
void ClientConnection::showMessage(const QString &logLine) {
using enum messages::ClientError;
if (m_parent.isMinimized() || m_parent.isHidden()) {
m_parent.showNormal();
m_parent.activateWindow();
result = true;
}
emit messageShowing();
return result;
}
const auto address = m_appConfig.serverHostname();
auto message =
QString("<p>The connection to server '%1' didn't work.</p>").arg(address);
QString ClientConnection::getMessage(const QString &line) const {
QString message(QObject::tr("Connection failed.\nCheck the IP address on the "
"server, your TLS and firewall settings."));
if (line.contains("server already has a connected client with our name")) {
message =
QObject::tr("Connection failed.\nYou can’t name 2 computers the same.");
if (logLine.contains("server already has a connected client with our name")) {
m_deps->showError(m_pParent, AlreadyConnected, address);
} else if (QHostAddress a(address); a.isNull()) {
qDebug("ip not detected, showing hostname error");
m_deps->showError(m_pParent, HostnameError, address);
} else {
QHostAddress address(m_appConfig.serverHostname());
if (address.isNull()) {
message =
QObject::tr(
"We can’t connect to the server \"%1\" try to connect using the "
"server IP address and check your firewall settings.")
.arg(m_appConfig.serverHostname());
qDebug("ip detected, showing generic error");
m_deps->showError(m_pParent, GenericError, address);
}
}
return message;
}
void ClientConnection::showMessage(const QString &message) const {
QMessageBox dialog(&m_parent);
dialog.addButton(QObject::tr("Close"), QMessageBox::RejectRole);
dialog.setText(message);
dialog.exec();
}
void ClientConnection::setCheckConnection(bool checkConnection) {
m_checkConnection = checkConnection;
}
} // namespace synergy::gui

View file

@ -17,27 +17,50 @@
#pragma once
#include "gui/config/AppConfig.h"
#include "gui/config/IAppConfig.h"
#include "gui/messages.h"
#include <QObject>
#include <QString>
#include <QWidget>
#include <memory>
class QWidget;
namespace synergy::gui {
class ClientConnection {
class ClientConnection : public QObject {
Q_OBJECT
public:
explicit ClientConnection(QWidget &parent, AppConfig &appConfig);
void update(const QString &line);
void setCheckConnection(bool checkConnection);
struct Deps {
virtual ~Deps() = default;
virtual void showError(
QWidget *parent, messages::ClientError error,
const QString &address) const;
};
explicit ClientConnection(
QWidget *parent, IAppConfig &appConfig,
std::shared_ptr<Deps> deps = std::make_shared<Deps>())
: m_pParent(parent),
m_appConfig(appConfig),
m_deps(deps) {}
void handleLogLine(const QString &line);
void setShowMessage() { m_showMessage = true; }
signals:
void messageShowing();
private:
QString getMessage(const QString &line) const;
bool checkMainWindow();
void showMessage(const QString &message) const;
void showMessage(const QString &logLine);
QWidget &m_parent;
AppConfig &m_appConfig;
bool m_checkConnection = false;
QWidget *m_pParent;
IAppConfig &m_appConfig;
std::shared_ptr<Deps> m_deps;
bool m_showMessage = true;
};
} // namespace synergy::gui

View file

@ -18,42 +18,46 @@
#include "ServerConnection.h"
#include "ServerMessage.h"
#include "gui/config/AppConfig.h"
#include "messages.h"
#include <QMessageBox>
#include <QPushButton>
namespace synergy::gui {
ServerConnection::ServerConnection(
QWidget &parent, AppConfig &appConfig, IServerConfig &serverConfig)
: m_parent(parent),
m_appConfig(appConfig),
m_serverConfig(serverConfig) {}
//
// ServerConnection::Deps
//
void ServerConnection::update(const QString &line) {
ServerMessage message(line);
messages::NewClientPromptResult ServerConnection::Deps::showNewClientPrompt(
QWidget *parent, const QString &clientName) const {
return messages::showNewClientPrompt(parent, clientName);
}
//
// ServerConnection
//
ServerConnection::ServerConnection(
QWidget *parent, IAppConfig &appConfig, IServerConfig &serverConfig,
std::shared_ptr<Deps> deps)
: m_pParent(parent),
m_appConfig(appConfig),
m_serverConfig(serverConfig),
m_pDeps(deps) {}
void ServerConnection::handleLogLine(const QString &logLine) {
ServerMessage message(logLine);
if (!m_appConfig.useExternalConfig() && message.isNewClientMessage() &&
!m_ignoredClients.contains(message.getClientName())) {
addClient(message.getClientName());
handleNewClient(message.getClientName());
}
}
// TOOD: merge duplicated code between client and server connection
bool ServerConnection::checkMainWindow() {
bool result = m_parent.isActiveWindow();
void ServerConnection::handleNewClient(const QString &clientName) {
using enum messages::NewClientPromptResult;
if (m_parent.isMinimized() || m_parent.isHidden()) {
m_parent.showNormal();
m_parent.activateWindow();
result = true;
}
return result;
}
void ServerConnection::addClient(const QString &clientName) {
if (m_serverConfig.isFull()) {
qDebug(
"server config full, skipping add client prompt for: %s",
@ -68,30 +72,17 @@ void ServerConnection::addClient(const QString &clientName) {
return;
}
if (!checkMainWindow()) {
qDebug(
"main window not active, skipping add client prompt for: %s",
qPrintable(clientName));
return;
}
emit messageShowing();
QMessageBox message(&m_parent);
const QPushButton *ignore =
message.addButton("Ignore", QMessageBox::RejectRole);
const QPushButton *add =
message.addButton("Add client", QMessageBox::AcceptRole);
message.setText(
QString("A new client called '%1' wants to connect").arg(clientName));
message.exec();
if (message.clickedButton() == add) {
const auto result = m_pDeps->showNewClientPrompt(m_pParent, clientName);
if (result == Add) {
qDebug("accepted dialog, adding client: %s", qPrintable(clientName));
emit configureClient(clientName);
} else if (message.clickedButton() == ignore) {
} else if (result == Ignore) {
qDebug("declined dialog, ignoring client: %s", qPrintable(clientName));
m_ignoredClients.append(clientName);
} else {
qFatal("no expected dialog button was clicked");
qFatal("unexpected add client result");
}
}

View file

@ -20,8 +20,9 @@
#include <QString>
#include <QStringList>
#include "gui/config/AppConfig.h"
#include "gui/config/IAppConfig.h"
#include "gui/config/IServerConfig.h"
#include "gui/messages.h"
namespace synergy::gui {
@ -30,20 +31,28 @@ class ServerConnection : public QObject {
using IServerConfig = synergy::gui::IServerConfig;
public:
struct Deps {
virtual ~Deps() = default;
virtual messages::NewClientPromptResult
showNewClientPrompt(QWidget *parent, const QString &clientName) const;
};
explicit ServerConnection(
QWidget &parent, AppConfig &appConfig, IServerConfig &serverConfig);
void update(const QString &line);
QWidget *parent, IAppConfig &appConfig, IServerConfig &serverConfig,
std::shared_ptr<Deps> deps = std::make_shared<Deps>());
void handleLogLine(const QString &logLine);
signals:
void messageShowing();
void configureClient(const QString &clientName);
private:
void addClient(const QString &clientName);
bool checkMainWindow();
void handleNewClient(const QString &clientName);
QWidget &m_parent;
AppConfig &m_appConfig;
QWidget *m_pParent;
IAppConfig &m_appConfig;
IServerConfig &m_serverConfig;
std::shared_ptr<Deps> m_pDeps;
QStringList m_ignoredClients;
};

View file

@ -21,7 +21,7 @@ namespace synergy::gui {
ServerMessage::ServerMessage(const QString &message)
: m_message(message),
m_clienName(parseClientName(message)) {}
m_clientName(parseClientName(message)) {}
bool ServerMessage::isNewClientMessage() const {
return m_message.contains("unrecognised client name");
@ -39,7 +39,7 @@ bool ServerMessage::isDisconnectedMessage() const {
return m_message.contains("has disconnected");
}
const QString &ServerMessage::getClientName() const { return m_clienName; }
const QString &ServerMessage::getClientName() const { return m_clientName; }
QString ServerMessage::parseClientName(const QString &line) const {
QString clientName("Unknown");

View file

@ -23,7 +23,7 @@ namespace synergy::gui {
class ServerMessage {
QString m_message;
QString m_clienName;
QString m_clientName;
public:
explicit ServerMessage(const QString &message);

View file

@ -25,6 +25,7 @@
#include <QDateTime>
#include <QDir>
#include <QMessageBox>
#include <QPushButton>
#include <QTime>
#include <memory>
@ -181,4 +182,59 @@ void showDevThanks(QWidget *parent, const QString &productName) {
kColorSecondary));
}
void showClientConnectError(
QWidget *parent, ClientError error, const QString &address) {
using enum ClientError;
auto message =
QString("<p>The connection to server '%1' didn't work.</p>").arg(address);
if (error == AlreadyConnected) {
message += //
"<p>Two of your client computers have the same name or there are "
"two instances of the client process running.</p>"
"<p>Please ensure that you're using a unique name and that only a "
"single client process is running.</p>";
} else if (error == HostnameError) {
message += //
"<p>Please try to connect to the server using the server IP address "
"instead of the hostname. </p>"
"<p>If that doesn't work, please check your TLS and "
"firewall settings.</p>";
} else if (error == GenericError) {
message += //
"<p>Please check your TLS and firewall settings.</p>";
} else {
qFatal("unknown client error");
}
QMessageBox dialog(parent);
dialog.addButton(QObject::tr("Close"), QMessageBox::RejectRole);
dialog.setText(message);
dialog.exec();
}
NewClientPromptResult
showNewClientPrompt(QWidget *parent, const QString &clientName) {
using enum NewClientPromptResult;
QMessageBox message(parent);
const QPushButton *ignore =
message.addButton("Ignore", QMessageBox::RejectRole);
const QPushButton *add =
message.addButton("Add client", QMessageBox::AcceptRole);
message.setText(
QString("A new client called '%1' wants to connect").arg(clientName));
message.exec();
if (message.clickedButton() == add) {
return Add;
} else if (message.clickedButton() == ignore) {
return Ignore;
} else {
qFatal("no expected dialog button was clicked");
abort();
}
}
} // namespace synergy::gui::messages

View file

@ -19,10 +19,15 @@
#include <QMessageLogContext>
#include <QString>
#include <QWidget>
class QWidget;
namespace synergy::gui::messages {
enum class ClientError { AlreadyConnected, HostnameError, GenericError };
enum class NewClientPromptResult { Add, Ignore };
void messageHandler(
QtMsgType type, const QMessageLogContext &context, const QString &msg);
@ -33,4 +38,10 @@ void showCloseReminder(QWidget *parent);
void showDevThanks(QWidget *parent, const QString &productName);
void showClientConnectError(
QWidget *parent, ClientError error, const QString &address);
NewClientPromptResult
showNewClientPrompt(QWidget *parent, const QString &clientName);
} // namespace synergy::gui::messages

View file

@ -0,0 +1,61 @@
/*
* 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 "QSettingsProxy.h"
namespace synergy::gui::proxy {
int QSettingsProxy::beginReadArray(const QString &prefix) {
return m_pSettings->beginReadArray(prefix);
}
void QSettingsProxy::setArrayIndex(int i) { m_pSettings->setArrayIndex(i); }
QVariant QSettingsProxy::value(const QString &key) const {
return m_pSettings->value(key);
}
QVariant
QSettingsProxy::value(const QString &key, const QVariant &defaultValue) const {
return m_pSettings->value(key, defaultValue);
}
void QSettingsProxy::endArray() { m_pSettings->endArray(); }
void QSettingsProxy::beginWriteArray(const QString &prefix) {
m_pSettings->beginWriteArray(prefix);
}
void QSettingsProxy::setValue(const QString &key, const QVariant &value) {
m_pSettings->setValue(key, value);
}
void QSettingsProxy::beginGroup(const QString &prefix) {
m_pSettings->beginGroup(prefix);
}
void QSettingsProxy::remove(const QString &key) { m_pSettings->remove(key); }
void QSettingsProxy::endGroup() { m_pSettings->endGroup(); }
bool QSettingsProxy::isWritable() const { return m_pSettings->isWritable(); }
bool QSettingsProxy::contains(const QString &key) const {
return m_pSettings->contains(key);
}
} // namespace synergy::gui::proxy

View file

@ -0,0 +1,48 @@
/*
* 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/>.
*/
#pragma once
#include <QSettings>
namespace synergy::gui::proxy {
class QSettingsProxy {
public:
virtual ~QSettingsProxy() = default;
virtual int beginReadArray(const QString &prefix);
virtual void beginWriteArray(const QString &prefix);
virtual void setArrayIndex(int i);
virtual QVariant value(const QString &key) const;
virtual QVariant
value(const QString &key, const QVariant &defaultValue) const;
virtual void endArray();
virtual void setValue(const QString &key, const QVariant &value);
virtual void beginGroup(const QString &prefix);
virtual void endGroup();
virtual void remove(const QString &key);
virtual bool isWritable() const;
virtual bool contains(const QString &key) const;
void set(QSettings &settings) { m_pSettings = &settings; }
QSettings &get() const { return *m_pSettings; }
private:
QSettings *m_pSettings;
};
} // namespace synergy::gui::proxy

View file

@ -16,13 +16,12 @@
*/
#include "gui/core/CoreTool.h"
#include "shared/gui/QtCoreTest.h"
#include "shared/gui/TestQtCoreApp.h"
#include <gtest/gtest.h>
class CoreToolTests : public QtCoreTest {};
TEST_F(CoreToolTests, getProfileDir_noMock_returnsNonEmpty) {
TEST(CoreToolTests, getProfileDir_noMock_returnsNonEmpty) {
TestQtCoreApp app;
CoreTool coreTool;
QString profileDir = coreTool.getProfileDir();
@ -30,7 +29,8 @@ TEST_F(CoreToolTests, getProfileDir_noMock_returnsNonEmpty) {
EXPECT_FALSE(profileDir.isEmpty());
}
TEST_F(CoreToolTests, getInstalledDir_noMock_returnsNonEmpty) {
TEST(CoreToolTests, getInstalledDir_noMock_returnsNonEmpty) {
TestQtCoreApp app;
CoreTool coreTool;
QString installedDir = coreTool.getInstalledDir();
@ -38,7 +38,8 @@ TEST_F(CoreToolTests, getInstalledDir_noMock_returnsNonEmpty) {
EXPECT_FALSE(installedDir.isEmpty());
}
TEST_F(CoreToolTests, getArch_noMock_returnsNonEmpty) {
TEST(CoreToolTests, getArch_noMock_returnsNonEmpty) {
TestQtCoreApp app;
CoreTool coreTool;
QString arch = coreTool.getArch();
@ -46,7 +47,8 @@ TEST_F(CoreToolTests, getArch_noMock_returnsNonEmpty) {
EXPECT_FALSE(arch.isEmpty());
}
TEST_F(CoreToolTests, getSerialKeyFilePath_noMock_returnsNonEmpty) {
TEST(CoreToolTests, getSerialKeyFilePath_noMock_returnsNonEmpty) {
TestQtCoreApp app;
CoreTool coreTool;
QString serialKeyFilePath = coreTool.getSerialKeyFilePath();

View file

@ -1,37 +0,0 @@
/*
* 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/>.
*/
#pragma once
#include "gui/messages.h"
#include <QCoreApplication>
#include <gtest/gtest.h>
class QtCoreTest : public ::testing::Test {
public:
static void SetUpTestSuite() {
char **argv = nullptr;
int argc = 0;
s_app = std::make_unique<QCoreApplication>(argc, argv);
qInstallMessageHandler(synergy::gui::messages::messageHandler);
}
static void TearDownTestSuite() { s_app.reset(); }
static std::unique_ptr<QCoreApplication> s_app;
};

View file

@ -15,10 +15,14 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifdef QT_GUI_LIB
#pragma once
#include "QtTest.h"
#include <QCoreApplication>
std::unique_ptr<QApplication> QtTest::s_app;
int argc = 0; // NOLINT
char **argv = nullptr; // NOLINT
#endif
class TestQtCoreApp : public QCoreApplication {
public:
explicit TestQtCoreApp() : QCoreApplication(argc, argv) {}
};

View file

@ -15,10 +15,17 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifdef QT_GUI_LIB
#pragma once
#include "QtCoreTest.h"
#include <QApplication>
std::unique_ptr<QCoreApplication> QtCoreTest::s_app;
int argc = 0; // NOLINT
char **argv = nullptr; // NOLINT
#endif
/**
* Prefer using `TestQtCoreApp` instead.
*/
class TestQtFullApp : public QApplication {
public:
explicit TestQtFullApp() : QApplication(argc, argv) {}
};

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/>.
*/
#pragma once
#include "gui/config/IAppConfig.h"
#include "gui/config/ElevateMode.h"
#include <gmock/gmock.h>
class AppConfigMock : public synergy::gui::IAppConfig {
using ProcessMode = synergy::gui::ProcessMode;
public:
AppConfigMock() {
ON_CALL(*this, screenName()).WillByDefault(testing::ReturnRef(m_stubName));
ON_CALL(*this, networkInterface())
.WillByDefault(testing::ReturnRef(m_stubInterface));
ON_CALL(*this, logLevelText())
.WillByDefault(testing::Return("stub log level"));
}
MOCK_METHOD(QString, tlsCertPath, (), (const, override));
MOCK_METHOD(int, tlsKeyLength, (), (const, override));
MOCK_METHOD(bool, tlsEnabled, (), (const, override));
MOCK_METHOD(ProcessMode, processMode, (), (const, override));
MOCK_METHOD(ElevateMode, elevateMode, (), (const, override));
MOCK_METHOD(QString, logLevelText, (), (const, override));
MOCK_METHOD(const QString &, screenName, (), (const, override));
MOCK_METHOD(bool, preventSleep, (), (const, override));
MOCK_METHOD(bool, logToFile, (), (const, override));
MOCK_METHOD(const QString &, logFilename, (), (const, override));
MOCK_METHOD(QString, coreServerName, (), (const, override));
MOCK_METHOD(QString, coreClientName, (), (const, override));
MOCK_METHOD(bool, invertConnection, (), (const, override));
MOCK_METHOD(void, persistLogDir, (), (const, override));
MOCK_METHOD(QString, serialKey, (), (const, override));
MOCK_METHOD(bool, languageSync, (), (const, override));
MOCK_METHOD(bool, invertScrollDirection, (), (const, override));
MOCK_METHOD(int, port, (), (const, override));
MOCK_METHOD(bool, useExternalConfig, (), (const, override));
MOCK_METHOD(const QString &, configFile, (), (const, override));
MOCK_METHOD(const QString &, networkInterface, (), (const, override));
MOCK_METHOD(const QString &, serverHostname, (), (const, override));
private:
const QString m_stubName = "stub name";
const QString m_stubInterface = "stub interface";
const QString m_stubAddress = "stub address";
};

View file

@ -17,21 +17,17 @@
#pragma once
#include "gui/messages.h"
#include "gui/config/IServerConfig.h"
#include <QApplication>
#include <gtest/gtest.h>
#include <gmock/gmock.h>
class QtTest : public ::testing::Test {
class ServerConfigMock : public synergy::gui::IServerConfig {
public:
static void SetUpTestSuite() {
char **argv = nullptr;
int argc = 0;
s_app = std::make_unique<QApplication>(argc, argv);
qInstallMessageHandler(synergy::gui::messages::messageHandler);
}
static void TearDownTestSuite() { s_app.reset(); }
static std::unique_ptr<QApplication> s_app;
MOCK_METHOD(bool, isFull, (), (const, override));
MOCK_METHOD(
bool, screenExists, (const QString &screenName), (const, override));
MOCK_METHOD(bool, save, (const QString &fileName), (const, override));
MOCK_METHOD(void, save, (QFile & file), (const, override));
MOCK_METHOD(bool, enableDragAndDrop, (), (const, override));
MOCK_METHOD(const ScreenList &, screens, (), (const, override));
};

View file

@ -17,15 +17,20 @@
#include "gui/config/AppConfig.h"
#include "gui/proxy/QSettingsProxy.h"
#include "gmock/gmock.h"
#include <gmock/gmock.h>
#include <gtest/gtest.h>
using namespace testing;
using namespace synergy::gui::proxy;
namespace {
class MockScopes : public synergy::gui::IConfigScopes {
class ConfigScopesMock : public synergy::gui::IConfigScopes {
using QSettingsProxy = synergy::gui::proxy::QSettingsProxy;
public:
MOCK_METHOD(void, signalReady, (), (override));
MOCK_METHOD(
@ -41,18 +46,19 @@ public:
MOCK_METHOD(Scope, activeScope, (), (const, override));
MOCK_METHOD(void, setActiveScope, (Scope scope), (override));
MOCK_METHOD(bool, isActiveScopeWritable, (), (const, override));
MOCK_METHOD(QSettings *, activeSettings, (), (const, override));
MOCK_METHOD(const QSettingsProxy &, activeSettings, (), (const, override));
MOCK_METHOD(QSettingsProxy &, activeSettings, (), (override));
MOCK_METHOD(void, save, (), (override));
};
struct MockDeps : public AppConfig::Deps {
MockDeps() {
struct DepsMock : public AppConfig::Deps {
DepsMock() {
ON_CALL(*this, profileDir()).WillByDefault(Return("stub"));
ON_CALL(*this, hostname()).WillByDefault(Return("stub"));
}
static std::shared_ptr<NiceMock<MockDeps>> makeNice() {
return std::make_shared<NiceMock<MockDeps>>();
static std::shared_ptr<NiceMock<DepsMock>> makeNice() {
return std::make_shared<NiceMock<DepsMock>>();
}
MOCK_METHOD(QString, profileDir, (), (const, override));
@ -64,8 +70,8 @@ struct MockDeps : public AppConfig::Deps {
class AppConfigTests : public Test {};
TEST_F(AppConfigTests, ctor_byDefault_screenNameIsHostname) {
NiceMock<MockScopes> scopes;
auto deps = MockDeps::makeNice();
NiceMock<ConfigScopesMock> scopes;
auto deps = DepsMock::makeNice();
ON_CALL(*deps, hostname()).WillByDefault(Return("test hostname"));
AppConfig appConfig(scopes, deps);
@ -74,8 +80,8 @@ TEST_F(AppConfigTests, ctor_byDefault_screenNameIsHostname) {
}
TEST_F(AppConfigTests, ctor_byDefault_getsFromScope) {
NiceMock<MockScopes> scopes;
auto deps = MockDeps::makeNice();
NiceMock<ConfigScopesMock> scopes;
auto deps = DepsMock::makeNice();
ON_CALL(scopes, scopeContains(_, _)).WillByDefault(Return(true));
ON_CALL(scopes, getFromScope(_, _, _))
@ -88,8 +94,8 @@ TEST_F(AppConfigTests, ctor_byDefault_getsFromScope) {
}
TEST_F(AppConfigTests, commit_byDefault_setsToScope) {
NiceMock<MockScopes> scopes;
auto deps = MockDeps::makeNice();
NiceMock<ConfigScopesMock> scopes;
auto deps = DepsMock::makeNice();
AppConfig appConfig(scopes, deps);
ON_CALL(scopes, isActiveScopeWritable()).WillByDefault(Return(true));

View file

@ -0,0 +1,68 @@
/*
* 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/config/Screen.h"
#include "gui/proxy/QSettingsProxy.h"
#include "shared/gui/TestQtCoreApp.h"
#include <gmock/gmock.h>
#include <gtest/gtest.h>
using namespace synergy::gui::proxy;
using namespace testing;
class QSettingsProxyMock : public QSettingsProxy {
public:
MOCK_METHOD(int, beginReadArray, (const QString &prefix), (override));
MOCK_METHOD(void, beginWriteArray, (const QString &prefix), (override));
MOCK_METHOD(void, setArrayIndex, (int i), (override));
MOCK_METHOD(QVariant, value, (const QString &key), (const, override));
MOCK_METHOD(
QVariant, value, (const QString &key, const QVariant &defaultValue),
(const, override));
MOCK_METHOD(void, endArray, (), (override));
MOCK_METHOD(
void, setValue, (const QString &key, const QVariant &value), (override));
MOCK_METHOD(void, beginGroup, (const QString &prefix), (override));
MOCK_METHOD(void, endGroup, (), (override));
MOCK_METHOD(void, remove, (const QString &key), (override));
MOCK_METHOD(bool, isWritable, (), (const, override));
MOCK_METHOD(bool, contains, (const QString &key), (const, override));
};
TEST(ScreenTests, loadSettings_whenHasSetting_readsArray) {
TestQtCoreApp app;
NiceMock<QSettingsProxyMock> settings;
Screen screen;
ON_CALL(settings, value(_)).WillByDefault(Return("stub"));
EXPECT_CALL(settings, beginReadArray(_)).Times(4);
screen.loadSettings(settings);
}
TEST(ScreenTests, saveSettings_whenNameIsSet_writesArray) {
TestQtCoreApp app;
NiceMock<QSettingsProxyMock> settings;
Screen screen;
screen.setName("stub");
EXPECT_CALL(settings, beginWriteArray(_)).Times(4);
screen.saveSettings(settings);
}

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/ClientConnection.h"
#include "shared/gui/mocks/AppConfigMock.h"
#include <gmock/gmock.h>
#include <gtest/gtest.h>
class QWidget;
using testing::_;
using testing::NiceMock;
using namespace synergy::gui;
using enum messages::ClientError;
namespace {
struct DepsMock : public ClientConnection::Deps {
MOCK_METHOD(
void, showError,
(QWidget * parent, messages::ClientError error, const QString &address),
(const, override));
};
} // namespace
class ClientConnectionTests : public testing::Test {
public:
ClientConnectionTests() {
ON_CALL(m_appConfig, serverHostname())
.WillByDefault(testing::ReturnRef(stub));
}
std::shared_ptr<DepsMock> m_pDeps = std::make_shared<NiceMock<DepsMock>>();
NiceMock<AppConfigMock> m_appConfig;
private:
const QString stub = "stub";
};
TEST_F(ClientConnectionTests, handleLogLine_alreadyConnected_showError) {
ClientConnection clientConnection(nullptr, m_appConfig, m_pDeps);
const QString serverName = "test server";
ON_CALL(m_appConfig, serverHostname())
.WillByDefault(testing::ReturnRef(serverName));
EXPECT_CALL(*m_pDeps, showError(_, AlreadyConnected, serverName));
clientConnection.handleLogLine(
"failed to connect to server\n"
"server already has a connected client with our name");
}
TEST_F(ClientConnectionTests, handleLogLine_withHostname_showError) {
ClientConnection clientConnection(nullptr, m_appConfig, m_pDeps);
const QString serverName = "test-hostname";
ON_CALL(m_appConfig, serverHostname())
.WillByDefault(testing::ReturnRef(serverName));
EXPECT_CALL(*m_pDeps, showError(_, HostnameError, serverName));
clientConnection.handleLogLine("failed to connect to server");
}
TEST_F(ClientConnectionTests, handleLogLine_withIpAddress_showError) {
ClientConnection clientConnection(nullptr, m_appConfig, m_pDeps);
const QString serverName = "1.1.1.1";
ON_CALL(m_appConfig, serverHostname())
.WillByDefault(testing::ReturnRef(serverName));
EXPECT_CALL(*m_pDeps, showError(_, GenericError, serverName));
clientConnection.handleLogLine("failed to connect to server");
}
TEST_F(ClientConnectionTests, handleLogLine_messageShown_shouldNotShowAgain) {
ClientConnection clientConnection(nullptr, m_appConfig, m_pDeps);
clientConnection.handleLogLine("failed to connect to server");
EXPECT_CALL(*m_pDeps, showError(_, _, _)).Times(0);
clientConnection.handleLogLine("failed to connect to server");
}
TEST_F(
ClientConnectionTests,
handleLogLine_serverRefusedClient_shouldNotShowError) {
ClientConnection clientConnection(nullptr, m_appConfig, m_pDeps);
EXPECT_CALL(*m_pDeps, showError(_, _, _)).Times(0);
clientConnection.handleLogLine("failed to connect to server\n"
"server refused client with our name");
}
TEST_F(
ClientConnectionTests, handleLogLine_connected_shouldPreventFutureError) {
ClientConnection clientConnection(nullptr, m_appConfig, m_pDeps);
clientConnection.handleLogLine("connected to server");
EXPECT_CALL(*m_pDeps, showError(_, _, _)).Times(0);
clientConnection.handleLogLine("failed to connect to server");
}
TEST_F(ClientConnectionTests, handleLogLine_otherMessage_shouldNotShowError) {
ClientConnection clientConnection(nullptr, m_appConfig, m_pDeps);
EXPECT_CALL(*m_pDeps, showError(_, _, _)).Times(0);
clientConnection.handleLogLine("hello world");
}

View file

@ -19,6 +19,8 @@
#include "gui/core/CoreProcess.h"
#include "gui/ipc/IQIpcClient.h"
#include "gui/proxy/QProcessProxy.h"
#include "shared/gui/mocks/AppConfigMock.h"
#include "shared/gui/mocks/ServerConfigMock.h"
#include "gmock/gmock.h"
#include <gmock/gmock.h>
@ -30,58 +32,13 @@ using ::testing::NiceMock;
using ::testing::Return;
using ::testing::ReturnRef;
class MockAppConfig : public IAppConfig {
public:
MockAppConfig() {
ON_CALL(*this, screenName()).WillByDefault(ReturnRef(m_stubName));
ON_CALL(*this, networkInterface())
.WillByDefault(ReturnRef(m_stubInterface));
ON_CALL(*this, logLevelText()).WillByDefault(Return("stub log level"));
}
namespace {
MOCK_METHOD(QString, tlsCertPath, (), (const, override));
MOCK_METHOD(int, tlsKeyLength, (), (const, override));
MOCK_METHOD(bool, tlsEnabled, (), (const, override));
MOCK_METHOD(ProcessMode, processMode, (), (const, override));
MOCK_METHOD(ElevateMode, elevateMode, (), (const, override));
MOCK_METHOD(QString, logLevelText, (), (const, override));
MOCK_METHOD(const QString &, screenName, (), (const, override));
MOCK_METHOD(bool, preventSleep, (), (const, override));
MOCK_METHOD(bool, logToFile, (), (const, override));
MOCK_METHOD(const QString &, logFilename, (), (const, override));
MOCK_METHOD(QString, coreServerName, (), (const, override));
MOCK_METHOD(QString, coreClientName, (), (const, override));
MOCK_METHOD(bool, invertConnection, (), (const, override));
MOCK_METHOD(void, persistLogDir, (), (const, override));
MOCK_METHOD(QString, serialKey, (), (const, override));
MOCK_METHOD(bool, languageSync, (), (const, override));
MOCK_METHOD(bool, invertScrollDirection, (), (const, override));
MOCK_METHOD(int, port, (), (const, override));
MOCK_METHOD(bool, useExternalConfig, (), (const, override));
MOCK_METHOD(const QString &, configFile, (), (const, override));
MOCK_METHOD(const QString &, networkInterface, (), (const, override));
private:
const QString m_stubName = "stub name";
const QString m_stubInterface = "stub interface";
const QString m_stubAddress = "stub address";
};
class MockServerConfig : public IServerConfig {
public:
MOCK_METHOD(bool, isFull, (), (const, override));
MOCK_METHOD(
bool, screenExists, (const QString &screenName), (const, override));
MOCK_METHOD(bool, save, (const QString &fileName), (const, override));
MOCK_METHOD(void, save, (QFile & file), (const, override));
MOCK_METHOD(bool, enableDragAndDrop, (), (const, override));
};
class MockQProcessProxy : public proxy::QProcessProxy {
class QProcessProxyMock : public proxy::QProcessProxy {
public:
operator bool() const override { return toBool(); }
MockQProcessProxy() {
QProcessProxyMock() {
ON_CALL(*this, toBool()).WillByDefault(Return(true));
ON_CALL(*this, state())
.WillByDefault(Return(QProcess::ProcessState::Running));
@ -100,9 +57,9 @@ public:
MOCK_METHOD(QString, readAllStandardError, (), (override));
};
class MockQIpcClient : public ipc::IQIpcClient {
class QIpcClientMock : public ipc::IQIpcClient {
public:
MockQIpcClient() {
QIpcClientMock() {
ON_CALL(*this, isConnected()).WillByDefault(Return(true));
}
@ -115,9 +72,9 @@ public:
MOCK_METHOD(bool, isConnected, (), (const, override));
};
class MockDeps : public CoreProcess::Deps {
class DepsMock : public CoreProcess::Deps {
public:
MockDeps() {
DepsMock() {
ON_CALL(*this, process()).WillByDefault(ReturnRef(m_process));
ON_CALL(*this, ipcClient()).WillByDefault(ReturnRef(m_ipcClient));
ON_CALL(*this, appPath(_)).WillByDefault(Return("stub app path"));
@ -131,21 +88,23 @@ public:
MOCK_METHOD(bool, fileExists, (const QString &path), (const, override));
MOCK_METHOD(QString, getProfileRoot, (), (const, override));
NiceMock<MockQProcessProxy> m_process;
NiceMock<MockQIpcClient> m_ipcClient;
NiceMock<QProcessProxyMock> m_process;
NiceMock<QIpcClientMock> m_ipcClient;
};
class CoreProcessTests : public ::testing::Test {
public:
CoreProcessTests() : m_coreProcess(m_appConfig, m_serverConfig, m_pDeps) {}
NiceMock<MockAppConfig> m_appConfig;
NiceMock<MockServerConfig> m_serverConfig;
std::shared_ptr<NiceMock<MockDeps>> m_pDeps =
std::make_shared<NiceMock<MockDeps>>();
NiceMock<AppConfigMock> m_appConfig;
NiceMock<ServerConfigMock> m_serverConfig;
std::shared_ptr<NiceMock<DepsMock>> m_pDeps =
std::make_shared<NiceMock<DepsMock>>();
CoreProcess m_coreProcess;
};
} // namespace
TEST_F(CoreProcessTests, start_serverDesktop_callsProcessStart) {
m_coreProcess.setMode(CoreProcess::Mode::Server);

View file

@ -0,0 +1,91 @@
/*
* 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/ServerConnection.h"
#include "shared/gui/mocks/AppConfigMock.h"
#include "shared/gui/mocks/ServerConfigMock.h"
#include "gmock/gmock.h"
#include <gmock/gmock.h>
#include <gtest/gtest.h>
using testing::_;
using testing::NiceMock;
using namespace synergy::gui;
class QWidget;
namespace {
struct DepsMock : public ServerConnection::Deps {
MOCK_METHOD(
messages::NewClientPromptResult, showNewClientPrompt,
(QWidget * parent, const QString &clientName), (const, override));
};
} // namespace
class ServerConnectionTests : public testing::Test {
public:
std::shared_ptr<DepsMock> m_pDeps = std::make_shared<NiceMock<DepsMock>>();
NiceMock<AppConfigMock> m_appConfig;
NiceMock<ServerConfigMock> m_serverConfig;
};
TEST_F(ServerConnectionTests, handleLogLine_newClient_shouldShowPrompt) {
ServerConnection serverConnection(
nullptr, m_appConfig, m_serverConfig, m_pDeps);
QString clientName = "test client";
EXPECT_CALL(*m_pDeps, showNewClientPrompt(_, clientName));
serverConnection.handleLogLine(R"(unrecognised client name "test client")");
}
TEST_F(ServerConnectionTests, handleLogLine_ignoredClient_shouldNotShowPrompt) {
ServerConnection serverConnection(
nullptr, m_appConfig, m_serverConfig, m_pDeps);
ON_CALL(*m_pDeps, showNewClientPrompt(_, _))
.WillByDefault(testing::Return(messages::NewClientPromptResult::Ignore));
serverConnection.handleLogLine(R"(unrecognised client name "stub")");
EXPECT_CALL(*m_pDeps, showNewClientPrompt(_, _)).Times(0);
serverConnection.handleLogLine(R"(unrecognised client name "stub")");
}
TEST_F(
ServerConnectionTests, handleLogLine_serverConfigFull_shouldNotShowPrompt) {
ServerConnection serverConnection(
nullptr, m_appConfig, m_serverConfig, m_pDeps);
ON_CALL(m_serverConfig, isFull()).WillByDefault(testing::Return(true));
EXPECT_CALL(*m_pDeps, showNewClientPrompt(_, _)).Times(0);
serverConnection.handleLogLine(R"(unrecognised client name "test client")");
}
TEST_F(ServerConnectionTests, handleLogLine_screenExists_shouldNotShowPrompt) {
ServerConnection serverConnection(
nullptr, m_appConfig, m_serverConfig, m_pDeps);
ON_CALL(m_serverConfig, screenExists(_)).WillByDefault(testing::Return(true));
EXPECT_CALL(*m_pDeps, showNewClientPrompt(_, _)).Times(0);
serverConnection.handleLogLine(R"(unrecognised client name "test client")");
}