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 - #7425 Refactored core process management and IPC client
- #7426 Fixed warnings and enable errors as warnings - #7426 Fixed warnings and enable errors as warnings
- #7427 More reliable button click detection for add client dialog - #7427 More reliable button click detection for add client dialog
- #7428 Refactor settings dialog to simplify enable/disable logic
# 1.14.6 # 1.14.6

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -40,12 +40,15 @@ using namespace synergy::gui;
const char *const kProProductName = "Synergy 1 Pro"; const char *const kProProductName = "Synergy 1 Pro";
SettingsDialog::SettingsDialog( 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), : QDialog(parent, Qt::WindowTitleHint | Qt::WindowSystemMenuHint),
Ui::SettingsDialogBase(), Ui::SettingsDialogBase(),
m_appConfig(config), m_appConfig(appConfig),
m_serverConfig(serverConfig),
m_license(license), m_license(license),
m_tlsUtility(config, license) { m_coreProcess(coreProcess),
m_tlsUtility(appConfig, license) {
setupUi(this); setupUi(this);
@ -53,124 +56,18 @@ SettingsDialog::SettingsDialog(
// the developer was looking at, and it's easy to accidentally save that. // the developer was looking at, and it's easy to accidentally save that.
m_pTabWidget->setCurrentIndex(0); m_pTabWidget->setCurrentIndex(0);
m_pMainWindow = dynamic_cast<MainWindow *>(parent);
loadFromConfig(); loadFromConfig();
m_wasOriginallySystemScope = appConfig().isActiveScopeSystem(); m_wasOriginallySystemScope = m_appConfig.isActiveScopeSystem();
updateControlsEnabled(); updateControls();
const auto &serveConfig = m_pMainWindow->serverConfig();
m_pScreenNameError = new validators::ValidationError(this); m_pScreenNameError = new validators::ValidationError(this);
m_pLineEditScreenName->setValidator(new validators::ScreenNameValidator( 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()) { // Auto-connect slots
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;
}
void SettingsDialog::on_m_pCheckBoxLogToFile_stateChanged(int i) { void SettingsDialog::on_m_pCheckBoxLogToFile_stateChanged(int i) {
bool checked = i == 2; bool checked = i == 2;
@ -206,9 +103,9 @@ void SettingsDialog::on_m_pCheckBoxEnableCrypto_clicked(bool) {
void SettingsDialog::on_m_pRadioSystemScope_toggled(bool checked) { void SettingsDialog::on_m_pRadioSystemScope_toggled(bool checked) {
// We only need to test the System scoped Radio as they are connected // We only need to test the System scoped Radio as they are connected
appConfig().setLoadFromSystemScope(checked); m_appConfig.setLoadFromSystemScope(checked);
loadFromConfig(); loadFromConfig();
updateControlsEnabled(); updateControls();
} }
void SettingsDialog::on_m_pPushButtonBrowseCert_clicked() { void SettingsDialog::on_m_pPushButtonBrowseCert_clicked() {
@ -233,18 +130,6 @@ void SettingsDialog::on_m_pComboBoxKeyLength_currentIndexChanged(int index) {
updateTlsRegenerateButton(); 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() { void SettingsDialog::on_m_pPushButtonRegenCert_clicked() {
if (m_tlsUtility.generateCertificate()) { if (m_tlsUtility.generateCertificate()) {
QMessageBox::information( 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) { void SettingsDialog::updateKeyLengthOnFile(const QString &path) {
TlsCertificate ssl; TlsCertificate ssl;
if (!QFile(path).exists()) { if (!QFile(path).exists()) {
@ -262,52 +275,48 @@ void SettingsDialog::updateKeyLengthOnFile(const QString &path) {
auto length = ssl.getCertKeyLength(path); auto length = ssl.getCertKeyLength(path);
auto index = m_pComboBoxKeyLength->findText(QString::number(length)); auto index = m_pComboBoxKeyLength->findText(QString::number(length));
m_pComboBoxKeyLength->setCurrentIndex(index); m_pComboBoxKeyLength->setCurrentIndex(index);
appConfig().setTlsKeyLength(length); m_appConfig.setTlsKeyLength(length);
} }
void SettingsDialog::updateControlsEnabled() { void SettingsDialog::updateControls() {
bool writable = appConfig().isActiveScopeWritable();
#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_pLineEditScreenName->setEnabled(writable);
m_pSpinBoxPort->setEnabled(writable); m_pSpinBoxPort->setEnabled(writable);
m_pLineEditInterface->setEnabled(writable); m_pLineEditInterface->setEnabled(writable);
m_pComboLogLevel->setEnabled(writable); m_pComboLogLevel->setEnabled(writable);
m_pCheckBoxLogToFile->setEnabled(writable); m_pCheckBoxLogToFile->setEnabled(writable);
m_pComboElevate->setEnabled(writable);
m_pCheckBoxAutoHide->setEnabled(writable); m_pCheckBoxAutoHide->setEnabled(writable);
m_pCheckBoxPreventSleep->setEnabled(writable); m_pCheckBoxPreventSleep->setEnabled(writable);
m_pLineEditCertificatePath->setEnabled(writable); m_pLineEditCertificatePath->setEnabled(writable);
m_pComboBoxKeyLength->setEnabled(writable); m_pComboBoxKeyLength->setEnabled(writable);
m_pPushButtonBrowseCert->setEnabled(writable); m_pPushButtonBrowseCert->setEnabled(writable);
m_pCheckBoxEnableCrypto->setEnabled(writable); m_pCheckBoxEnableCrypto->setEnabled(writable);
m_pCheckBoxServiceEnabled->setEnabled(writable);
m_pCheckBoxCloseToTray->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_pCheckBoxLanguageSync->setEnabled(writable && isClientMode());
m_pCheckBoxScrollDirection->setEnabled(writable && isClientMode()); m_pCheckBoxScrollDirection->setEnabled(writable && isClientMode());
#if !defined(Q_OS_WIN) m_pLabelLogPath->setEnabled(writable && logToFile);
m_pCheckBoxServiceEnabled->setEnabled(false); m_pLineEditLogFilename->setEnabled(writable && logToFile);
#endif m_pButtonBrowseLog->setEnabled(writable && logToFile);
m_pLabelLogPath->setEnabled(writable && m_pCheckBoxLogToFile->isChecked());
m_pLineEditLogFilename->setEnabled(
writable && m_pCheckBoxLogToFile->isChecked());
m_pButtonBrowseLog->setEnabled(writable && m_pCheckBoxLogToFile->isChecked());
updateTlsControlsEnabled(); updateTlsControlsEnabled();
updateTlsRegenerateButton();
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(); updateTlsControls();
} }

View file

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

View file

@ -1,7 +1,6 @@
/* /*
* synergy -- mouse and keyboard sharing utility * synergy -- mouse and keyboard sharing utility
* Copyright (C) 2012-2021 Symless Ltd. * Copyright (C) 2021 Symless Ltd.
* Copyright (C) 2008 Volker Lanz (vl@fidra.de)
* *
* This package is free software; you can redistribute it and/or * This package is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License * 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 * You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>. * along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
#include "ScreenDuplicationsValidator.h" #include "ScreenDuplicationsValidator.h"
namespace validators { namespace validators {

View file

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

View file

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

View file

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

View file

@ -520,7 +520,7 @@ bool AppConfig::useInternalConfig() const { return m_UseInternalConfig; }
bool AppConfig::clientGroupChecked() const { return m_ClientGroupChecked; } 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; } void AppConfig::setActivationHasRun(bool value) { m_ActivationHasRun = value; }

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -1,6 +1,6 @@
/* /*
* synergy -- mouse and keyboard sharing utility * 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) * Copyright (C) 2008 Volker Lanz (vl@fidra.de)
* *
* This package is free software; you can redistribute it and/or * This package is free software; you can redistribute it and/or
@ -17,19 +17,19 @@
*/ */
#include "Screen.h" #include "Screen.h"
#include "config/ScreenConfig.h"
#include <QtCore> #include <QtCore>
#include <QtGui> #include <QtGui>
Screen::Screen() using namespace synergy::gui::proxy;
: m_Pixmap(QPixmap(":res/icons/64x64/video-display.png")), using enum ScreenConfig::Modifier;
m_Swapped(false) { using enum ScreenConfig::SwitchCorner;
init(); using enum ScreenConfig::Fix;
}
Screen::Screen(const QString &name) Screen::Screen() { init(); }
: m_Pixmap(QPixmap(":res/icons/64x64/video-display.png")),
m_Swapped(false) { Screen::Screen(const QString &name) {
init(); init();
setName(name); setName(name);
} }
@ -45,17 +45,17 @@ void Screen::init() {
// m_Modifiers, m_SwitchCorners and m_Fixes are QLists we use like fixed-size // 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 // arrays, thus we need to make sure to fill them with the required number of
// elements. // elements.
for (int i = 0; i < NumModifiers; i++) for (int i = 0; i < static_cast<int>(NumModifiers); i++)
modifiers() << i; modifiers() << i;
for (int i = 0; i < NumSwitchCorners; i++) for (int i = 0; i < static_cast<int>(NumSwitchCorners); i++)
switchCorners() << false; switchCorners() << false;
for (int i = 0; i < NumFixes; i++) for (int i = 0; i < static_cast<int>(NumFixes); i++)
fixes() << false; fixes() << false;
} }
void Screen::loadSettings(QSettings &settings) { void Screen::loadSettings(QSettingsProxy &settings) {
setName(settings.value("name").toString()); setName(settings.value("name").toString());
if (name().isEmpty()) if (name().isEmpty())
@ -66,13 +66,14 @@ void Screen::loadSettings(QSettings &settings) {
readSettings(settings, aliases(), "alias", QString("")); readSettings(settings, aliases(), "alias", QString(""));
readSettings( readSettings(
settings, modifiers(), "modifier", static_cast<int>(DefaultMod), settings, modifiers(), "modifier", static_cast<int>(DefaultMod),
NumModifiers); static_cast<int>(NumModifiers));
readSettings( readSettings(
settings, switchCorners(), "switchCorner", false, NumSwitchCorners); settings, switchCorners(), "switchCorner", 0,
readSettings(settings, fixes(), "fix", false, NumFixes); 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()); settings.setValue("name", name());
if (name().isEmpty()) 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_Fixes == screen.m_Fixes && m_Swapped == screen.m_Swapped &&
m_isServer == screen.m_isServer; 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 * 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) * Copyright (C) 2008 Volker Lanz (vl@fidra.de)
* *
* This package is free software; you can redistribute it and/or * This package is free software; you can redistribute it and/or
@ -20,6 +20,8 @@
#include "ScreenConfig.h" #include "ScreenConfig.h"
#include "gui/proxy/QSettingsProxy.h"
#include <QList> #include <QList>
#include <QPixmap> #include <QPixmap>
#include <QString> #include <QString>
@ -30,34 +32,49 @@ class QTextStream;
class ScreenSettingsDialog; class ScreenSettingsDialog;
class Screen : public ScreenConfig { class Screen : public ScreenConfig {
friend QDataStream &operator<<(QDataStream &outStream, const Screen &screen); using QSettingsProxy = synergy::gui::proxy::QSettingsProxy;
friend QDataStream &operator>>(QDataStream &inStream, Screen &screen);
friend class ScreenSettingsDialog; friend class ScreenSettingsDialog;
friend class ScreenSetupModel; friend class ScreenSetupModel;
friend class ScreenSetupView; friend class ScreenSetupView;
public: friend QDataStream &operator<<(QDataStream &outStream, const Screen &screen) {
Screen(); return outStream << screen.name() << screen.switchCornerSize()
Screen(const QString &name); << 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: public:
explicit Screen();
explicit Screen(const QString &name);
const QPixmap &pixmap() const { return m_Pixmap; } const QPixmap &pixmap() const { return m_Pixmap; }
const QString &name() const { return m_Name; } const QString &name() const { return m_Name; }
const QStringList &aliases() const { return m_Aliases; } const QStringList &aliases() const { return m_Aliases; }
bool isNull() const { return m_Name.isEmpty(); } bool isNull() const { return m_Name.isEmpty(); }
int modifier(int m) const { 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; } const QList<int> &modifiers() const { return m_Modifiers; }
bool switchCorner(int c) const { return m_SwitchCorners[c]; } bool switchCorner(int c) const { return m_SwitchCorners[c]; }
const QList<bool> &switchCorners() const { return m_SwitchCorners; } const QList<bool> &switchCorners() const { return m_SwitchCorners; }
int switchCornerSize() const { return m_SwitchCornerSize; } 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; } const QList<bool> &fixes() const { return m_Fixes; }
void loadSettings(QSettings &settings); void loadSettings(QSettingsProxy &settings);
void saveSettings(QSettings &settings) const; void saveSettings(QSettingsProxy &settings) const;
QTextStream &writeScreensSection(QTextStream &outStream) const; QTextStream &writeScreensSection(QTextStream &outStream) const;
QTextStream &writeAliasesSection(QTextStream &outStream) const; QTextStream &writeAliasesSection(QTextStream &outStream) const;
@ -84,18 +101,13 @@ protected:
void setSwapped(bool on) { m_Swapped = on; } void setSwapped(bool on) { m_Swapped = on; }
private: private:
QPixmap m_Pixmap; QPixmap m_Pixmap = QPixmap(":res/icons/64x64/video-display.png");
QString m_Name; QString m_Name;
QStringList m_Aliases; QStringList m_Aliases;
QList<int> m_Modifiers; QList<int> m_Modifiers;
QList<bool> m_SwitchCorners; QList<bool> m_SwitchCorners;
int m_SwitchCornerSize; int m_SwitchCornerSize;
QList<bool> m_Fixes; QList<bool> m_Fixes;
bool m_Swapped = false;
bool m_Swapped;
bool m_isServer = 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 * 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) * Copyright (C) 2008 Volker Lanz (vl@fidra.de)
* *
* This package is free software; you can redistribute it and/or * This package is free software; you can redistribute it and/or

View file

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

View file

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

View file

@ -17,71 +17,67 @@
#include "ClientConnection.h" #include "ClientConnection.h"
#include "messages.h"
#include <QHostAddress> #include <QHostAddress>
#include <QMessageBox> #include <QMessageBox>
namespace synergy::gui { namespace synergy::gui {
ClientConnection::ClientConnection(QWidget &parent, AppConfig &appConfig) //
: m_parent(parent), // ClientConnection::Deps
m_appConfig(appConfig) {} //
void ClientConnection::update(const QString &line) { void ClientConnection::Deps::showError(
if (m_checkConnection && checkMainWindow()) { QWidget *parent, messages::ClientError error,
if (line.contains("failed to connect to server")) { const QString &address) const {
m_checkConnection = false; messages::showClientConnectError(parent, error, address);
if (!line.contains("server refused client with our name") && }
!line.contains("Trying next address")) {
showMessage(getMessage(line)); //
} // ClientConnection
} else if (line.contains("connected to server")) { //
m_checkConnection = false;
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() { void ClientConnection::showMessage(const QString &logLine) {
bool result = m_parent.isActiveWindow(); using enum messages::ClientError;
if (m_parent.isMinimized() || m_parent.isHidden()) { emit messageShowing();
m_parent.showNormal();
m_parent.activateWindow();
result = true;
}
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 { if (logLine.contains("server already has a connected client with our name")) {
QString message(QObject::tr("Connection failed.\nCheck the IP address on the " m_deps->showError(m_pParent, AlreadyConnected, address);
"server, your TLS and firewall settings.")); } else if (QHostAddress a(address); a.isNull()) {
qDebug("ip not detected, showing hostname error");
if (line.contains("server already has a connected client with our name")) { m_deps->showError(m_pParent, HostnameError, address);
message =
QObject::tr("Connection failed.\nYou can’t name 2 computers the same.");
} else { } else {
QHostAddress address(m_appConfig.serverHostname()); qDebug("ip detected, showing generic error");
if (address.isNull()) { m_deps->showError(m_pParent, GenericError, address);
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());
}
} }
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 } // namespace synergy::gui

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -25,6 +25,7 @@
#include <QDateTime> #include <QDateTime>
#include <QDir> #include <QDir>
#include <QMessageBox> #include <QMessageBox>
#include <QPushButton>
#include <QTime> #include <QTime>
#include <memory> #include <memory>
@ -181,4 +182,59 @@ void showDevThanks(QWidget *parent, const QString &productName) {
kColorSecondary)); 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 } // namespace synergy::gui::messages

View file

@ -19,10 +19,15 @@
#include <QMessageLogContext> #include <QMessageLogContext>
#include <QString> #include <QString>
#include <QWidget>
class QWidget;
namespace synergy::gui::messages { namespace synergy::gui::messages {
enum class ClientError { AlreadyConnected, HostnameError, GenericError };
enum class NewClientPromptResult { Add, Ignore };
void messageHandler( void messageHandler(
QtMsgType type, const QMessageLogContext &context, const QString &msg); QtMsgType type, const QMessageLogContext &context, const QString &msg);
@ -33,4 +38,10 @@ void showCloseReminder(QWidget *parent);
void showDevThanks(QWidget *parent, const QString &productName); 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 } // 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 "gui/core/CoreTool.h"
#include "shared/gui/QtCoreTest.h" #include "shared/gui/TestQtCoreApp.h"
#include <gtest/gtest.h> #include <gtest/gtest.h>
class CoreToolTests : public QtCoreTest {}; TEST(CoreToolTests, getProfileDir_noMock_returnsNonEmpty) {
TestQtCoreApp app;
TEST_F(CoreToolTests, getProfileDir_noMock_returnsNonEmpty) {
CoreTool coreTool; CoreTool coreTool;
QString profileDir = coreTool.getProfileDir(); QString profileDir = coreTool.getProfileDir();
@ -30,7 +29,8 @@ TEST_F(CoreToolTests, getProfileDir_noMock_returnsNonEmpty) {
EXPECT_FALSE(profileDir.isEmpty()); EXPECT_FALSE(profileDir.isEmpty());
} }
TEST_F(CoreToolTests, getInstalledDir_noMock_returnsNonEmpty) { TEST(CoreToolTests, getInstalledDir_noMock_returnsNonEmpty) {
TestQtCoreApp app;
CoreTool coreTool; CoreTool coreTool;
QString installedDir = coreTool.getInstalledDir(); QString installedDir = coreTool.getInstalledDir();
@ -38,7 +38,8 @@ TEST_F(CoreToolTests, getInstalledDir_noMock_returnsNonEmpty) {
EXPECT_FALSE(installedDir.isEmpty()); EXPECT_FALSE(installedDir.isEmpty());
} }
TEST_F(CoreToolTests, getArch_noMock_returnsNonEmpty) { TEST(CoreToolTests, getArch_noMock_returnsNonEmpty) {
TestQtCoreApp app;
CoreTool coreTool; CoreTool coreTool;
QString arch = coreTool.getArch(); QString arch = coreTool.getArch();
@ -46,7 +47,8 @@ TEST_F(CoreToolTests, getArch_noMock_returnsNonEmpty) {
EXPECT_FALSE(arch.isEmpty()); EXPECT_FALSE(arch.isEmpty());
} }
TEST_F(CoreToolTests, getSerialKeyFilePath_noMock_returnsNonEmpty) { TEST(CoreToolTests, getSerialKeyFilePath_noMock_returnsNonEmpty) {
TestQtCoreApp app;
CoreTool coreTool; CoreTool coreTool;
QString serialKeyFilePath = coreTool.getSerialKeyFilePath(); 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/>. * 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/>. * 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 #pragma once
#include "gui/messages.h" #include "gui/config/IServerConfig.h"
#include <QApplication> #include <gmock/gmock.h>
#include <gtest/gtest.h>
class QtTest : public ::testing::Test { class ServerConfigMock : public synergy::gui::IServerConfig {
public: public:
static void SetUpTestSuite() { MOCK_METHOD(bool, isFull, (), (const, override));
char **argv = nullptr; MOCK_METHOD(
int argc = 0; bool, screenExists, (const QString &screenName), (const, override));
s_app = std::make_unique<QApplication>(argc, argv); MOCK_METHOD(bool, save, (const QString &fileName), (const, override));
qInstallMessageHandler(synergy::gui::messages::messageHandler); MOCK_METHOD(void, save, (QFile & file), (const, override));
} MOCK_METHOD(bool, enableDragAndDrop, (), (const, override));
MOCK_METHOD(const ScreenList &, screens, (), (const, override));
static void TearDownTestSuite() { s_app.reset(); }
static std::unique_ptr<QApplication> s_app;
}; };

View file

@ -17,15 +17,20 @@
#include "gui/config/AppConfig.h" #include "gui/config/AppConfig.h"
#include "gui/proxy/QSettingsProxy.h"
#include "gmock/gmock.h" #include "gmock/gmock.h"
#include <gmock/gmock.h> #include <gmock/gmock.h>
#include <gtest/gtest.h> #include <gtest/gtest.h>
using namespace testing; using namespace testing;
using namespace synergy::gui::proxy;
namespace { namespace {
class MockScopes : public synergy::gui::IConfigScopes { class ConfigScopesMock : public synergy::gui::IConfigScopes {
using QSettingsProxy = synergy::gui::proxy::QSettingsProxy;
public: public:
MOCK_METHOD(void, signalReady, (), (override)); MOCK_METHOD(void, signalReady, (), (override));
MOCK_METHOD( MOCK_METHOD(
@ -41,18 +46,19 @@ public:
MOCK_METHOD(Scope, activeScope, (), (const, override)); MOCK_METHOD(Scope, activeScope, (), (const, override));
MOCK_METHOD(void, setActiveScope, (Scope scope), (override)); MOCK_METHOD(void, setActiveScope, (Scope scope), (override));
MOCK_METHOD(bool, isActiveScopeWritable, (), (const, 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)); MOCK_METHOD(void, save, (), (override));
}; };
struct MockDeps : public AppConfig::Deps { struct DepsMock : public AppConfig::Deps {
MockDeps() { DepsMock() {
ON_CALL(*this, profileDir()).WillByDefault(Return("stub")); ON_CALL(*this, profileDir()).WillByDefault(Return("stub"));
ON_CALL(*this, hostname()).WillByDefault(Return("stub")); ON_CALL(*this, hostname()).WillByDefault(Return("stub"));
} }
static std::shared_ptr<NiceMock<MockDeps>> makeNice() { static std::shared_ptr<NiceMock<DepsMock>> makeNice() {
return std::make_shared<NiceMock<MockDeps>>(); return std::make_shared<NiceMock<DepsMock>>();
} }
MOCK_METHOD(QString, profileDir, (), (const, override)); MOCK_METHOD(QString, profileDir, (), (const, override));
@ -64,8 +70,8 @@ struct MockDeps : public AppConfig::Deps {
class AppConfigTests : public Test {}; class AppConfigTests : public Test {};
TEST_F(AppConfigTests, ctor_byDefault_screenNameIsHostname) { TEST_F(AppConfigTests, ctor_byDefault_screenNameIsHostname) {
NiceMock<MockScopes> scopes; NiceMock<ConfigScopesMock> scopes;
auto deps = MockDeps::makeNice(); auto deps = DepsMock::makeNice();
ON_CALL(*deps, hostname()).WillByDefault(Return("test hostname")); ON_CALL(*deps, hostname()).WillByDefault(Return("test hostname"));
AppConfig appConfig(scopes, deps); AppConfig appConfig(scopes, deps);
@ -74,8 +80,8 @@ TEST_F(AppConfigTests, ctor_byDefault_screenNameIsHostname) {
} }
TEST_F(AppConfigTests, ctor_byDefault_getsFromScope) { TEST_F(AppConfigTests, ctor_byDefault_getsFromScope) {
NiceMock<MockScopes> scopes; NiceMock<ConfigScopesMock> scopes;
auto deps = MockDeps::makeNice(); auto deps = DepsMock::makeNice();
ON_CALL(scopes, scopeContains(_, _)).WillByDefault(Return(true)); ON_CALL(scopes, scopeContains(_, _)).WillByDefault(Return(true));
ON_CALL(scopes, getFromScope(_, _, _)) ON_CALL(scopes, getFromScope(_, _, _))
@ -88,8 +94,8 @@ TEST_F(AppConfigTests, ctor_byDefault_getsFromScope) {
} }
TEST_F(AppConfigTests, commit_byDefault_setsToScope) { TEST_F(AppConfigTests, commit_byDefault_setsToScope) {
NiceMock<MockScopes> scopes; NiceMock<ConfigScopesMock> scopes;
auto deps = MockDeps::makeNice(); auto deps = DepsMock::makeNice();
AppConfig appConfig(scopes, deps); AppConfig appConfig(scopes, deps);
ON_CALL(scopes, isActiveScopeWritable()).WillByDefault(Return(true)); 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/core/CoreProcess.h"
#include "gui/ipc/IQIpcClient.h" #include "gui/ipc/IQIpcClient.h"
#include "gui/proxy/QProcessProxy.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"
#include <gmock/gmock.h> #include <gmock/gmock.h>
@ -30,58 +32,13 @@ using ::testing::NiceMock;
using ::testing::Return; using ::testing::Return;
using ::testing::ReturnRef; using ::testing::ReturnRef;
class MockAppConfig : public IAppConfig { namespace {
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"));
}
MOCK_METHOD(QString, tlsCertPath, (), (const, override)); class QProcessProxyMock : public proxy::QProcessProxy {
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 {
public: public:
operator bool() const override { return toBool(); } operator bool() const override { return toBool(); }
MockQProcessProxy() { QProcessProxyMock() {
ON_CALL(*this, toBool()).WillByDefault(Return(true)); ON_CALL(*this, toBool()).WillByDefault(Return(true));
ON_CALL(*this, state()) ON_CALL(*this, state())
.WillByDefault(Return(QProcess::ProcessState::Running)); .WillByDefault(Return(QProcess::ProcessState::Running));
@ -100,9 +57,9 @@ public:
MOCK_METHOD(QString, readAllStandardError, (), (override)); MOCK_METHOD(QString, readAllStandardError, (), (override));
}; };
class MockQIpcClient : public ipc::IQIpcClient { class QIpcClientMock : public ipc::IQIpcClient {
public: public:
MockQIpcClient() { QIpcClientMock() {
ON_CALL(*this, isConnected()).WillByDefault(Return(true)); ON_CALL(*this, isConnected()).WillByDefault(Return(true));
} }
@ -115,9 +72,9 @@ public:
MOCK_METHOD(bool, isConnected, (), (const, override)); MOCK_METHOD(bool, isConnected, (), (const, override));
}; };
class MockDeps : public CoreProcess::Deps { class DepsMock : public CoreProcess::Deps {
public: public:
MockDeps() { DepsMock() {
ON_CALL(*this, process()).WillByDefault(ReturnRef(m_process)); ON_CALL(*this, process()).WillByDefault(ReturnRef(m_process));
ON_CALL(*this, ipcClient()).WillByDefault(ReturnRef(m_ipcClient)); ON_CALL(*this, ipcClient()).WillByDefault(ReturnRef(m_ipcClient));
ON_CALL(*this, appPath(_)).WillByDefault(Return("stub app path")); 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(bool, fileExists, (const QString &path), (const, override));
MOCK_METHOD(QString, getProfileRoot, (), (const, override)); MOCK_METHOD(QString, getProfileRoot, (), (const, override));
NiceMock<MockQProcessProxy> m_process; NiceMock<QProcessProxyMock> m_process;
NiceMock<MockQIpcClient> m_ipcClient; NiceMock<QIpcClientMock> m_ipcClient;
}; };
class CoreProcessTests : public ::testing::Test { class CoreProcessTests : public ::testing::Test {
public: public:
CoreProcessTests() : m_coreProcess(m_appConfig, m_serverConfig, m_pDeps) {} CoreProcessTests() : m_coreProcess(m_appConfig, m_serverConfig, m_pDeps) {}
NiceMock<MockAppConfig> m_appConfig; NiceMock<AppConfigMock> m_appConfig;
NiceMock<MockServerConfig> m_serverConfig; NiceMock<ServerConfigMock> m_serverConfig;
std::shared_ptr<NiceMock<MockDeps>> m_pDeps = std::shared_ptr<NiceMock<DepsMock>> m_pDeps =
std::make_shared<NiceMock<MockDeps>>(); std::make_shared<NiceMock<DepsMock>>();
CoreProcess m_coreProcess; CoreProcess m_coreProcess;
}; };
} // namespace
TEST_F(CoreProcessTests, start_serverDesktop_callsProcessStart) { TEST_F(CoreProcessTests, start_serverDesktop_callsProcessStart) {
m_coreProcess.setMode(CoreProcess::Mode::Server); 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")");
}