Refactor local/global save/load and show dev thanks message (#7423)

* WIP: Show dev thanks message

* Improve error messages and guard license code

* Print core command

* Allow license tests when licensing disabled by default

* Don't check invalid serial key

* Fixed typo

* Add error checking

* Use shared ptr instead of static deps

* Actually save and load the setting

* Add dev thanks vars to CI

* Show thanks message after connected

* Refactor hack-job of local/global save/load to remove coupling and use Qt events system

* Simplify config loading

* Overloads for optionals

* Fixed global config scope not loaded

* Save message state before showing to avoid multiple messages

* Update ChangeLog

* Fixed lint warnings

* Make function const

* Reduce verbosity

* Remove dead code, show version on start, tidy up lint warnings

* Make product name code safer

* Connect on enter press

* Only show server specific wording when on server

* Add emit commands

* Log info and error

* Remove deaad code

* Remove test member

* Disable start context menu action unless usable

* Show more relvant connect message

* Fixed close to tray not applied, and simplified restart core logic

* Remove redundant include
This commit is contained in:
Nick Bolton
2024-08-03 01:17:29 +01:00
committed by GitHub
parent e0c08852bf
commit 157fe818d8
50 changed files with 1525 additions and 1186 deletions

View File

@ -27,6 +27,7 @@ env:
SYNERGY_PRODUCT_NAME: ${{ vars.SYNERGY_PRODUCT_NAME }}
SYNERGY_PACKAGE_PREFIX: ${{ vars.SYNERGY_PACKAGE_PREFIX }}
SYNERGY_ENABLE_LICENSING: ${{ vars.SYNERGY_ENABLE_LICENSING }}
SYNERGY_SHOW_DEV_THANKS: ${{ vars.SYNERGY_SHOW_DEV_THANKS }}
PACKAGE_BUILD: ${{ !github.event.pull_request.draft }}
PACKAGE_UPLOAD: ${{ !github.event.pull_request.draft && github.event_name != 'schedule' }}
UPLOAD_TO_GITHUB: ${{ github.event_name == 'pull_request' && !github.event.pull_request.draft }}

View File

@ -66,6 +66,7 @@ Enhancements:
- #7421 Only load core after settings have fully loaded
- #7419 Introduce 'Advanced' tab to Preferences window
- #7422 Handle empty value for computer name setting
- #7423 Refactor local/global save/load and show dev thanks message
# 1.14.6

View File

@ -60,6 +60,11 @@ macro(configure_definitions)
add_definitions(-DSYNERGY_PRODUCT_NAME="${PRODUCT_NAME}")
endif()
if(SHOW_DEV_THANKS)
message(STATUS "Enabling dev thanks message")
add_definitions(-DSYNERGY_SHOW_DEV_THANKS=1)
endif()
if(NOT CMAKE_BUILD_TYPE STREQUAL "Debug")
message(STATUS "Disabling debug build")
add_definitions(-DNDEBUG)
@ -108,6 +113,9 @@ macro(configure_options)
# licensing is off by default to make life easier for contributors.
set(DEFAULT_ENABLE_LICENSING OFF)
# by default, show the dev thanks message.
set(DEFAULT_SHOW_DEV_THANKS ON)
if("$ENV{SYNERGY_BUILD_MINIMAL}" STREQUAL "true")
set(DEFAULT_BUILD_GUI OFF)
set(DEFAULT_BUILD_INSTALLER OFF)
@ -129,11 +137,16 @@ macro(configure_options)
set(DEFAULT_ENABLE_COVERAGE ON)
endif()
if("$ENV{SYNERGY_SHOW_DEV_THANKS}" STREQUAL "false")
set(DEFAULT_SHOW_DEV_THANKS OFF)
endif()
option(BUILD_GUI "Build GUI" ${DEFAULT_BUILD_GUI})
option(BUILD_INSTALLER "Build installer" ${DEFAULT_BUILD_INSTALLER})
option(BUILD_TESTS "Build tests" ${DEFAULT_BUILD_TESTS})
option(BUILD_UNIFIED "Build unified binary" ${DEFAULT_BUILD_UNIFIED})
option(ENABLE_LICENSING "Enable licensing" ${DEFAULT_ENABLE_LICENSING})
option(ENABLE_COVERAGE "Enable test coverage" ${DEFAULT_ENABLE_COVERAGE})
option(SHOW_DEV_THANKS "Show dev thanks message" ${DEFAULT_SHOW_DEV_THANKS})
endmacro()

View File

@ -24,23 +24,14 @@
#include "OSXHelpers.h"
#endif
#ifdef GIT_SHA_SHORT
const QString kVersionAppend = GIT_SHA_SHORT;
#else
const QString kVersionAppend;
#endif
#include "gui/version.h"
AboutDialog::AboutDialog(MainWindow *parent, const AppConfig &config)
AboutDialog::AboutDialog(MainWindow *parent)
: QDialog(parent, Qt::WindowTitleHint | Qt::WindowSystemMenuHint),
Ui::AboutDialogBase() {
setupUi(this);
QString version(SYNERGY_VERSION);
if (!kVersionAppend.isEmpty()) {
version.append(QString(" (%1)").arg(kVersionAppend));
}
m_pLabelSynergyVersion->setText(version);
m_pLabelSynergyVersion->setText(synergy::gui::version());
QString buildDateString = QString::fromLocal8Bit(__DATE__).simplified();
QDate buildDate = QLocale("en_US").toDate(buildDateString, "MMM d yyyy");

View File

@ -30,7 +30,7 @@ class QString;
class AboutDialog : public QDialog, public Ui::AboutDialogBase {
Q_OBJECT
public:
AboutDialog(MainWindow *parent, const AppConfig &config);
explicit AboutDialog(MainWindow *parent);
int exec() override;
private:

View File

@ -23,6 +23,7 @@
#include "gui/LicenseHandler.h"
#include "gui/constants.h"
#include "gui/license_notices.h"
#include "gui/styles.h"
#include "license/ProductEdition.h"
#include "license/parse_serial_key.h"
#include "ui_ActivationDialog.h"
@ -107,7 +108,6 @@ void ActivationDialog::accept() {
}
showSuccessDialog();
m_pAppConfig->setActivationHasRun(true);
QDialog::accept();
}
@ -122,6 +122,7 @@ void ActivationDialog::showResultDialog(
QMessageBox::information(
this, title,
"Heads up, the serial key you entered was the same as last time.");
QDialog::accept();
break;
case kInvalid:

View File

@ -22,11 +22,14 @@
#include "ActivationDialog.h"
#include "ServerConfigDialog.h"
#include "SettingsDialog.h"
#include "gui/ConfigScopes.h"
#include "gui/LicenseHandler.h"
#include "gui/TlsFingerprint.h"
#include "gui/VersionChecker.h"
#include "gui/constants.h"
#include "gui/license_notices.h"
#include "gui/messages.h"
#include "gui/styles.h"
#include "license/License.h"
#if defined(Q_OS_MAC)
@ -58,12 +61,6 @@ using namespace synergy::license;
static const int kRetryDelay = 1000;
static const int kDebugLogLevel = 1;
#ifdef SYNERGY_PRODUCT_NAME
const QString kProductName = SYNERGY_PRODUCT_NAME;
#else
const QString kProductName;
#endif
#if defined(Q_OS_MAC)
static const char *const kLightIconFiles[] = {
@ -89,9 +86,10 @@ static const char *const kDefaultIconFiles[] = {
":/res/icons/16x16/synergy-transfering.png",
":/res/icons/16x16/synergy-disconnected.png"};
MainWindow::MainWindow(AppConfig &appConfig)
: m_AppConfig(appConfig),
m_ServerConfig(5, 3, &m_AppConfig, this),
MainWindow::MainWindow(ConfigScopes &configScopes, AppConfig &appConfig)
: m_ConfigScopes(configScopes),
m_AppConfig(appConfig),
m_ServerConfig(appConfig, *this),
m_ServerConnection(*this),
m_ClientConnection(*this),
m_TlsUtility(appConfig, m_LicenseHandler.license()),
@ -101,21 +99,13 @@ MainWindow::MainWindow(AppConfig &appConfig)
setupControls();
connectSlots();
#if defined(Q_OS_WIN)
// TODO: only connect permenantly to ipc when switching to service mode.
// if switching from service to desktop, connect only to stop the service
// and don't retry.
m_IpcClient.connectToHost();
#endif
// handled by `onCreated`
emit created();
}
MainWindow::~MainWindow() {
try {
if (appConfig().processMode() == ProcessMode::kDesktop) {
if (m_AppConfig.processMode() == ProcessMode::kDesktop) {
m_ExpectedRunningState = RuningState::Stopped;
stopDesktop();
}
@ -132,18 +122,16 @@ MainWindow::~MainWindow() {
void MainWindow::restoreWindow() {
const auto &config = appConfig();
const auto &size = config.mainWindowSize();
if (size.has_value()) {
const auto &windowSize = m_AppConfig.mainWindowSize();
if (windowSize.has_value()) {
qDebug("restoring main window size");
resize(size.value());
resize(windowSize.value());
}
const auto &position = config.mainWindowPosition();
if (position.has_value()) {
const auto &windowPosition = m_AppConfig.mainWindowPosition();
if (windowPosition.has_value()) {
qDebug("restoring main window position");
move(position.value());
move(windowPosition.value());
}
// give the window chance to restore its size and position before the window
@ -159,10 +147,9 @@ void MainWindow::saveWindow() {
}
qDebug("saving window size and position");
auto &config = appConfig();
config.setMainWindowSize(size());
config.setMainWindowPosition(pos());
config.saveSettings();
m_AppConfig.setMainWindowSize(size());
m_AppConfig.setMainWindowPosition(pos());
m_ConfigScopes.save();
}
void MainWindow::setupControls() {
@ -210,7 +197,8 @@ void MainWindow::connectSlots() const {
Qt::QueuedConnection);
connect(
&m_AppConfig, &AppConfig::loaded, this, &MainWindow::onAppConfigLoaded);
&m_ConfigScopes, &ConfigScopes::saving, this,
&MainWindow::onConfigScopesSaving, Qt::DirectConnection);
connect(
&m_AppConfig, &AppConfig::tlsChanged, this,
@ -267,20 +255,30 @@ void MainWindow::connectSlots() const {
&MainWindow::onWindowSaveTimerTimeout);
}
void MainWindow::onAppAboutToQuit() { m_AppConfig.saveSettings(); }
void MainWindow::onAppAboutToQuit() { m_ConfigScopes.save(); }
void MainWindow::onCreated() {
// it may seem a bit over-complicated to load the app config through a
// signal/slot, but there is a good reason: intentionality; we want to
// ensure that the ctor is fully executed before we start loading the
// app config. a signal is the clearest way to communicate this.
m_AppConfig.loadAllScopes();
const auto serverEnabled = appConfig().serverGroupChecked();
const auto clientEnabled = appConfig().clientGroupChecked();
if (serverEnabled || clientEnabled) {
startCore();
#if defined(Q_OS_WIN)
// TODO: only connect permenantly to ipc when switching to service mode.
// if switching from service to desktop, connect only to stop the service
// and don't retry.
m_IpcClient.connectToHost();
#endif
m_ConfigScopes.signalReady();
applyCloseToTray();
if (kLicensingEnabled && !m_AppConfig.serialKey().isEmpty()) {
m_LicenseHandler.changeSerialKey(m_AppConfig.serialKey());
}
updateScreenName();
applyConfig();
restoreWindow();
}
void MainWindow::onShown() {
@ -299,7 +297,7 @@ void MainWindow::onLicenseHandlerSerialKeyChanged(const QString &serialKey) {
if (m_AppConfig.serialKey() != serialKey) {
m_AppConfig.setSerialKey(serialKey);
m_AppConfig.saveSettings();
m_ConfigScopes.save();
}
}
@ -308,16 +306,7 @@ void MainWindow::onLicenseHandlerInvalidLicense() {
showActivationDialog();
}
void MainWindow::onAppConfigLoaded() {
QApplication::setQuitOnLastWindowClosed(!m_AppConfig.closeToTray());
if (!m_AppConfig.serialKey().isEmpty()) {
m_LicenseHandler.changeSerialKey(m_AppConfig.serialKey());
}
updateScreenName();
applyConfig();
restoreWindow();
}
void MainWindow::onConfigScopesSaving() { m_ServerConfig.commit(); }
void MainWindow::onAppConfigTlsChanged() {
updateLocalFingerprint();
@ -421,7 +410,7 @@ bool MainWindow::on_m_pActionSave_triggered() {
QString fileName =
QFileDialog::getSaveFileName(this, QString("Save configuration as..."));
if (!fileName.isEmpty() && !serverConfig().save(fileName)) {
if (!fileName.isEmpty() && !m_ServerConfig.save(fileName)) {
QMessageBox::warning(
this, QString("Save failed"),
QString("Could not save configuration to file."));
@ -432,8 +421,8 @@ bool MainWindow::on_m_pActionSave_triggered() {
}
void MainWindow::on_m_pActionAbout_triggered() {
AboutDialog dlg(this, appConfig());
dlg.exec();
AboutDialog about(this);
about.exec();
}
void MainWindow::on_m_pActionHelp_triggered() {
@ -442,13 +431,14 @@ void MainWindow::on_m_pActionHelp_triggered() {
void MainWindow::on_m_pActionSettings_triggered() {
auto result =
SettingsDialog(this, appConfig(), m_LicenseHandler.license()).exec();
SettingsDialog(this, m_AppConfig, m_LicenseHandler.license()).exec();
if (result == QDialog::Accepted) {
enableServer(appConfig().serverGroupChecked());
enableClient(appConfig().clientGroupChecked());
auto state = m_CoreState;
if ((state == CoreState::Connected) || (state == CoreState::Connecting) ||
(state == CoreState::Listening)) {
m_ConfigScopes.save();
applyConfig();
applyCloseToTray();
if (isCoreActive()) {
restartCore();
}
}
@ -460,6 +450,14 @@ void MainWindow::on_m_pButtonConfigureServer_clicked() {
void MainWindow::on_m_pActivate_triggered() { showActivationDialog(); }
void MainWindow::on_m_pLineEditHostname_returnPressed() {
m_pButtonConnect->click();
}
void MainWindow::on_m_pLineEditClientIp_returnPressed() {
m_pButtonConnectToClient->click();
}
void MainWindow::on_m_pButtonApply_clicked() {
m_ClientConnection.setCheckConnection(true);
restartCore();
@ -477,13 +475,13 @@ void MainWindow::on_m_pLabelFingerprint_linkActivated(const QString &) {
void MainWindow::on_m_pRadioGroupServer_clicked(bool) {
enableServer(true);
enableClient(false);
m_AppConfig.saveSettings();
m_ConfigScopes.save();
}
void MainWindow::on_m_pRadioGroupClient_clicked(bool) {
enableClient(true);
enableServer(false);
m_AppConfig.saveSettings();
m_ConfigScopes.save();
}
void MainWindow::on_m_pButtonConnect_clicked() { on_m_pButtonApply_clicked(); }
@ -527,7 +525,7 @@ void MainWindow::open() {
setIcon(CoreState::Disconnected);
});
if (appConfig().autoHide()) {
if (m_AppConfig.autoHide()) {
hide();
} else {
showNormal();
@ -538,8 +536,8 @@ void MainWindow::open() {
// only start if user has previously started. this stops the gui from
// auto hiding before the user has configured synergy (which of course
// confuses first time users, who think synergy has crashed).
if (appConfig().startedBefore() &&
appConfig().processMode() == ProcessMode::kDesktop) {
if (m_AppConfig.startedBefore() &&
m_AppConfig.processMode() == ProcessMode::kDesktop) {
startCore();
}
}
@ -580,20 +578,24 @@ void MainWindow::createMenuBar() {
}
void MainWindow::applyConfig() {
enableServer(appConfig().serverGroupChecked());
enableClient(appConfig().clientGroupChecked());
enableServer(m_AppConfig.serverGroupChecked());
enableClient(m_AppConfig.clientGroupChecked());
m_pLineEditHostname->setText(appConfig().serverHostname());
m_pLineEditClientIp->setText(serverConfig().getClientAddress());
m_pLineEditHostname->setText(m_AppConfig.serverHostname());
m_pLineEditClientIp->setText(m_ServerConfig.getClientAddress());
}
void MainWindow::applyCloseToTray() const {
QApplication::setQuitOnLastWindowClosed(!m_AppConfig.closeToTray());
}
void MainWindow::saveSettings() {
appConfig().setServerGroupChecked(m_pRadioGroupServer->isChecked());
appConfig().setClientGroupChecked(m_pRadioGroupClient->isChecked());
appConfig().setServerHostname(m_pLineEditHostname->text());
serverConfig().setClientAddress(m_pLineEditClientIp->text());
m_AppConfig.setServerGroupChecked(m_pRadioGroupServer->isChecked());
m_AppConfig.setClientGroupChecked(m_pRadioGroupClient->isChecked());
m_AppConfig.setServerHostname(m_pLineEditHostname->text());
m_ServerConfig.setClientAddress(m_pLineEditClientIp->text());
appConfig().scopes().saveAll();
m_ConfigScopes.save();
}
void MainWindow::setIcon(CoreState state) const {
@ -622,19 +624,22 @@ void MainWindow::setIcon(CoreState state) const {
}
void MainWindow::appendLogInfo(const QString &text) {
qInfo("%s", text.toStdString().c_str());
processCoreLogLine(getTimeStamp() + " INFO: " + text);
}
void MainWindow::appendLogDebug(const QString &text) {
qDebug("%s", text.toStdString().c_str());
if (appConfig().logLevel() >= kDebugLogLevel) {
if (m_AppConfig.logLevel() >= kDebugLogLevel) {
processCoreLogLine(getTimeStamp() + " DEBUG: " + text);
}
}
void MainWindow::appendLogError(const QString &text) {
onIpcClientReadLogLine(getTimeStamp() + " ERROR: " + text);
qCritical("%s", text.toStdString().c_str());
processCoreLogLine(getTimeStamp() + " ERROR: " + text);
}
void MainWindow::processCoreLogLine(const QString &text) {
@ -644,7 +649,7 @@ void MainWindow::processCoreLogLine(const QString &text) {
}
// only start if there is no active service running
if (line.contains("service status: idle") && appConfig().startedBefore()) {
if (line.contains("service status: idle") && m_AppConfig.startedBefore()) {
startCore();
}
@ -689,15 +694,11 @@ void MainWindow::checkConnected(const QString &line) {
if (line.contains("connected to server") || line.contains("has connected")) {
setCoreState(CoreState::Connected);
if (!appConfig().startedBefore() && isVisible()) {
QMessageBox::information(
this, "Synergy",
QString("Synergy is now connected. You can close the "
"config window and Synergy will remain connected in "
"the background."));
appConfig().setStartedBefore(true);
if (isVisible()) {
showFirstRunMessage();
showDevThanksMessage();
}
} else if (line.contains("started server")) {
setCoreState(CoreState::Listening);
} else if (
@ -803,8 +804,53 @@ void MainWindow::showEvent(QShowEvent *event) {
emit shown();
}
void MainWindow::closeEvent(QCloseEvent *event) {
if (!m_AppConfig.closeToTray()) {
return;
}
qDebug("window will hide to tray");
if (!m_AppConfig.showCloseReminder()) {
return;
}
messages::showCloseReminder(this);
m_AppConfig.setShowCloseReminder(false);
m_ConfigScopes.save();
}
void MainWindow::showFirstRunMessage() {
if (m_AppConfig.startedBefore()) {
return;
}
m_AppConfig.setStartedBefore(true);
m_ConfigScopes.save();
const auto isServer = coreMode() == CoreMode::Server;
messages::showFirstRunMessage(
this, m_AppConfig.closeToTray(), m_AppConfig.enableService(), isServer);
}
void MainWindow::showDevThanksMessage() {
if (!m_AppConfig.showDevThanks()) {
qDebug("skipping dev thanks message, disabled in settings");
return;
}
if (kLicensingEnabled) {
qDebug("licensing enabled, skipping dev thanks message");
return;
}
m_AppConfig.setShowDevThanks(false);
m_ConfigScopes.save();
messages::showDevThanks(this, kProductName);
}
void MainWindow::startCore() {
appendLogDebug("starting core process");
appendLogInfo(QString("starting core %1 process").arg(coreModeString()));
saveSettings();
@ -828,11 +874,11 @@ void MainWindow::startCore() {
args << "-f"
<< "--no-tray"
<< "--debug" << appConfig().logLevelText();
<< "--debug" << m_AppConfig.logLevelText();
args << "--name" << appConfig().screenName();
args << "--name" << m_AppConfig.screenName();
ProcessMode mode = appConfig().processMode();
ProcessMode mode = m_AppConfig.processMode();
if (mode == ProcessMode::kDesktop) {
m_pCoreProcess = std::make_unique<QProcess>(this);
@ -850,7 +896,7 @@ void MainWindow::startCore() {
// unnecessary restarts when synergy was started elevated or
// when it is not allowed to elevate. In these cases restarting
// the server is fruitless.
if (appConfig().elevateMode() == ElevateAsNeeded) {
if (m_AppConfig.elevateMode() == ElevateAsNeeded) {
args << "--stop-on-desk-switch";
}
#endif
@ -896,10 +942,6 @@ void MainWindow::startCore() {
if (!m_pLogOutput->toPlainText().isEmpty())
onIpcClientReadLogLine("");
appendLogInfo(
"starting " +
QString(coreMode() == CoreMode::Server ? "server" : "client"));
if ((coreMode() == CoreMode::Client && !clientArgs(args, app)) ||
(coreMode() == CoreMode::Server && !serverArgs(args, app))) {
onActionStopCoreTriggered();
@ -918,15 +960,14 @@ void MainWindow::startCore() {
&MainWindow::onCoreProcessReadyReadStandardError);
}
// show command if debug log level...
if (appConfig().logLevel() >= 4) {
if (m_AppConfig.logLevel() >= kDebugLogLevel) {
appendLogInfo(QString("command: %1 %2").arg(app, args.join(" ")));
}
appendLogInfo("log level: " + appConfig().logLevelText());
appendLogInfo("log level: " + m_AppConfig.logLevelText());
if (appConfig().logToFile())
appendLogInfo("log file: " + appConfig().logFilename());
if (m_AppConfig.logToFile())
appendLogInfo("log file: " + m_AppConfig.logFilename());
if (mode == ProcessMode::kDesktop) {
m_pCoreProcess->start(app, args);
@ -944,12 +985,12 @@ void MainWindow::startCore() {
}
} else if (mode == ProcessMode::kService) {
QString command(app + " " + args.join(" "));
m_IpcClient.sendCommand(command, appConfig().elevateMode());
m_IpcClient.sendCommand(command, m_AppConfig.elevateMode());
}
}
bool MainWindow::clientArgs(QStringList &args, QString &app) {
app = appPath(appConfig().coreClientName());
app = appPath(m_AppConfig.coreClientName());
if (!QFile::exists(app)) {
show();
@ -959,22 +1000,22 @@ bool MainWindow::clientArgs(QStringList &args, QString &app) {
return false;
}
if (appConfig().logToFile()) {
appConfig().persistLogDir();
args << "--log" << appConfig().logFilename();
if (m_AppConfig.logToFile()) {
m_AppConfig.persistLogDir();
args << "--log" << m_AppConfig.logFilename();
}
if (appConfig().languageSync()) {
if (m_AppConfig.languageSync()) {
args << "--sync-language";
}
if (appConfig().invertScrollDirection()) {
if (m_AppConfig.invertScrollDirection()) {
args << "--invert-scroll";
}
if (appConfig().invertConnection()) {
if (m_AppConfig.invertConnection()) {
args << "--host";
args << ":" + QString::number(appConfig().port());
args << ":" + QString::number(m_AppConfig.port());
} else {
if (m_pLineEditHostname->text().isEmpty()) {
show();
@ -994,7 +1035,7 @@ bool MainWindow::clientArgs(QStringList &args, QString &app) {
hostName.push_back(']');
}
}
args << hostName + ":" + QString::number(appConfig().port());
args << hostName + ":" + QString::number(m_AppConfig.port());
}
return true;
@ -1002,8 +1043,8 @@ bool MainWindow::clientArgs(QStringList &args, QString &app) {
QString MainWindow::configFilename() {
QString configFullPath;
if (appConfig().useExternalConfig()) {
configFullPath = appConfig().configFile();
if (m_AppConfig.useExternalConfig()) {
configFullPath = m_AppConfig.configFile();
} else {
QStringList errors;
for (auto path :
@ -1023,7 +1064,7 @@ QString MainWindow::configFilename() {
continue;
}
serverConfig().save(configFile);
m_ServerConfig.save(configFile);
configFile.close();
configFullPath = configFile.fileName();
@ -1040,7 +1081,7 @@ QString MainWindow::configFilename() {
}
QString MainWindow::address() const {
QString i = appConfig().networkInterface();
QString i = m_AppConfig.networkInterface();
// if interface is IPv6 - ensure that ip is in square brackets
if (i.count(':') > 1) {
if (i[0] != '[') {
@ -1050,7 +1091,7 @@ QString MainWindow::address() const {
i.push_back(']');
}
}
return (!i.isEmpty() ? i : "") + ":" + QString::number(appConfig().port());
return (!i.isEmpty() ? i : "") + ":" + QString::number(m_AppConfig.port());
}
QString MainWindow::appPath(const QString &name) const {
@ -1059,7 +1100,7 @@ QString MainWindow::appPath(const QString &name) const {
}
bool MainWindow::serverArgs(QStringList &args, QString &app) {
app = appPath(appConfig().coreServerName());
app = appPath(m_AppConfig.coreServerName());
if (!QFile::exists(app)) {
QMessageBox::warning(
@ -1068,17 +1109,17 @@ bool MainWindow::serverArgs(QStringList &args, QString &app) {
return false;
}
if (appConfig().invertConnection() && m_pLineEditClientIp->text().isEmpty()) {
if (m_AppConfig.invertConnection() && m_pLineEditClientIp->text().isEmpty()) {
QMessageBox::warning(
this, QString("Client IP address or name is empty"),
QString("Please fill in a client IP address or name."));
return false;
}
if (appConfig().logToFile()) {
appConfig().persistLogDir();
if (m_AppConfig.logToFile()) {
m_AppConfig.persistLogDir();
args << "--log" << appConfig().logFilename();
args << "--log" << m_AppConfig.logFilename();
}
QString configFilename = this->configFilename();
@ -1089,8 +1130,8 @@ bool MainWindow::serverArgs(QStringList &args, QString &app) {
args << "-c" << configFilename << "--address" << address();
appendLogInfo("config file: " + configFilename);
if (kLicensingEnabled && !appConfig().serialKey().isEmpty()) {
args << "--serial-key" << appConfig().serialKey();
if (kLicensingEnabled && !m_AppConfig.serialKey().isEmpty()) {
args << "--serial-key" << m_AppConfig.serialKey();
}
return true;
@ -1101,9 +1142,9 @@ void MainWindow::stopCore() {
m_ExpectedRunningState = RuningState::Stopped;
if (appConfig().processMode() == ProcessMode::kService) {
if (m_AppConfig.processMode() == ProcessMode::kService) {
stopService();
} else if (appConfig().processMode() == ProcessMode::kDesktop) {
} else if (m_AppConfig.processMode() == ProcessMode::kDesktop) {
stopDesktop();
}
@ -1115,7 +1156,7 @@ void MainWindow::stopCore() {
void MainWindow::stopService() {
// send empty command to stop service from laucning anything.
m_IpcClient.sendCommand("", appConfig().elevateMode());
m_IpcClient.sendCommand("", m_AppConfig.elevateMode());
}
void MainWindow::stopDesktop() {
@ -1229,8 +1270,31 @@ void MainWindow::setVisible(bool visible) {
}
MainWindow::CoreMode MainWindow::coreMode() const {
auto isClient = m_pRadioGroupClient->isChecked();
return isClient ? CoreMode::Client : CoreMode::Server;
using enum CoreMode;
auto serverChecked = m_pRadioGroupServer->isChecked();
auto clientChecked = m_pRadioGroupClient->isChecked();
if (serverChecked) {
return Server;
} else if (clientChecked) {
return Client;
} else {
return None;
}
}
QString MainWindow::coreModeString() const {
using enum CoreMode;
switch (coreMode()) {
case Server:
return "server";
case Client:
return "client";
default:
qFatal("invalid core mode");
}
}
QString MainWindow::getIPAddresses() const {
@ -1291,22 +1355,22 @@ void MainWindow::updateLocalFingerprint() {
}
}
LicenseHandler &MainWindow::licenseHandler() { return m_LicenseHandler; }
void MainWindow::updateWindowTitle() {
QString MainWindow::productName() const {
if (kLicensingEnabled) {
setWindowTitle(m_LicenseHandler.productName());
return m_LicenseHandler.productName();
} else if (!kProductName.isEmpty()) {
return kProductName;
} else {
setWindowTitle(kProductName);
qFatal("product name not set");
}
}
void MainWindow::updateWindowTitle() { setWindowTitle(productName()); }
void MainWindow::autoAddScreen(const QString name) {
if (m_ActivationDialogRunning) {
// TODO: refactor this code
// add this screen to the pending list and check this list until
// users finish activation dialog
// add this screen to the pending list if the activation dialog is running.
m_PendingClientNames.append(name);
return;
}
@ -1316,7 +1380,7 @@ void MainWindow::autoAddScreen(const QString name) {
switch (r) {
case kAutoAddScreenManualServer:
showConfigureServer(QString("Please add the server (%1) to the grid.")
.arg(appConfig().screenName()));
.arg(m_AppConfig.screenName()));
break;
case kAutoAddScreenManualClient:
@ -1328,18 +1392,18 @@ void MainWindow::autoAddScreen(const QString name) {
}
}
void MainWindow::showConfigureServer(const QString &message) {
bool MainWindow::isCoreActive() const {
using enum CoreState;
ServerConfigDialog dlg(this, serverConfig(), appConfig());
dlg.message(message);
auto result = dlg.exec();
auto state = m_CoreState;
return (state == Connected) || (state == Connecting) || (state == Listening);
}
if (result == QDialog::Accepted) {
auto state = m_CoreState;
if ((state == Connected) || (state == Connecting) || (state == Listening)) {
restartCore();
}
void MainWindow::showConfigureServer(const QString &message) {
ServerConfigDialog dialog(this, serverConfig(), m_AppConfig);
dialog.message(message);
if ((dialog.exec() == QDialog::Accepted) && isCoreActive()) {
restartCore();
}
}
@ -1354,6 +1418,11 @@ int MainWindow::showActivationDialog() {
int result = activationDialog.exec();
m_ActivationDialogRunning = false;
if (result == QDialog::Accepted) {
m_AppConfig.setActivationHasRun(true);
m_ConfigScopes.save();
}
if (!m_PendingClientNames.empty()) {
foreach (const QString &name, m_PendingClientNames) {
autoAddScreen(name);
@ -1391,23 +1460,33 @@ void MainWindow::updateScreenName() {
m_pLabelComputerName->setText(
QString("This computer's name: %1 "
R"((<a href="#" style="color: %2">change</a>))")
.arg(appConfig().screenName())
.arg(m_AppConfig.screenName())
.arg(kColorSecondary));
serverConfig().updateServerName();
m_ServerConfig.updateServerName();
}
void MainWindow::enableServer(bool enable) {
qDebug(enable ? "server enabled" : "server disabled");
m_AppConfig.setServerGroupChecked(enable);
m_pRadioGroupServer->setChecked(enable);
m_pWidgetServer->setEnabled(enable);
m_pWidgetServerInverse->setVisible(m_AppConfig.invertConnection());
m_pButtonToggleStart->setEnabled(true);
if (enable) {
m_pButtonToggleStart->setEnabled(true);
m_pActionStartCore->setEnabled(true);
}
}
void MainWindow::enableClient(bool enable) {
qDebug(enable ? "client enabled" : "client disabled");
m_AppConfig.setClientGroupChecked(enable);
m_pRadioGroupClient->setChecked(enable);
m_pWidgetClient->setEnabled(enable);
m_pWidgetClient->setVisible(!m_AppConfig.invertConnection());
m_pButtonToggleStart->setEnabled(true);
if (enable) {
m_pButtonToggleStart->setEnabled(true);
m_pActionStartCore->setEnabled(true);
}
}

View File

@ -79,22 +79,23 @@ public:
PendingRetry
};
enum class CoreMode { Client, Server };
enum class CoreMode { None, Client, Server };
enum class LogLevel { Error, Info };
enum class RuningState { Started, Stopped };
public:
explicit MainWindow(AppConfig &appConfig);
explicit MainWindow(
synergy::gui::ConfigScopes &configScopes, AppConfig &appConfig);
~MainWindow() override;
void setVisible(bool visible) override;
CoreMode coreMode() const;
QString coreModeString() const;
QString address() const;
QString appPath(const QString &name) const;
void open();
ServerConfig &serverConfig() { return m_ServerConfig; }
void autoAddScreen(const QString name);
LicenseHandler &licenseHandler();
int showActivationDialog();
void appendLogInfo(const QString &text);
void appendLogDebug(const QString &text);
@ -110,7 +111,7 @@ public slots:
private slots:
void onCreated();
void onShown();
void onAppConfigLoaded();
void onConfigScopesSaving();
void onAppConfigTlsChanged();
void onAppConfigScreenNameChanged();
void onAppConfigInvertConnection();
@ -142,15 +143,17 @@ private slots:
void on_m_pActionHelp_triggered();
void on_m_pActionSettings_triggered();
void on_m_pActivate_triggered();
void on_m_pLineEditHostname_returnPressed();
void on_m_pLineEditClientIp_returnPressed();
private:
QSettings &settings() { return *appConfig().scopes().currentSettings(); }
AppConfig &appConfig() { return m_AppConfig; }
AppConfig const &appConfig() const { return m_AppConfig; }
void createMenuBar();
void createStatusBar();
void createTrayIcon();
void applyConfig();
void applyCloseToTray() const;
void setIcon(CoreState state) const;
void setCoreState(CoreState state);
bool checkForApp(int which, QString &app);
@ -172,6 +175,7 @@ private:
QString getTimeStamp() const;
void restartCore();
void showEvent(QShowEvent *) override;
void closeEvent(QCloseEvent *event) override;
void secureSocket(bool secureSocket);
void windowStateChanged();
void connectSlots() const;
@ -183,6 +187,7 @@ private:
void updateScreenName();
void saveSettings();
QString configFilename();
bool isCoreActive() const;
void showConfigureServer(const QString &message);
void showConfigureServer() { showConfigureServer(""); }
void showLicenseNotice();
@ -192,6 +197,9 @@ private:
void setupControls();
void resizeEvent(QResizeEvent *event) override;
void moveEvent(QMoveEvent *event) override;
void showFirstRunMessage();
void showDevThanksMessage();
QString productName() const;
#ifdef Q_OS_MAC
void checkOSXNotification(const QString &line);
@ -216,9 +224,10 @@ private:
bool m_SecureSocket = false;
QString m_SecureSocketVersion = "";
bool m_SaveWindow = false;
AppConfig &m_AppConfig;
LicenseHandler m_LicenseHandler;
synergy::gui::ConfigScopes &m_ConfigScopes;
AppConfig &m_AppConfig;
ServerConfig m_ServerConfig;
ServerConnection m_ServerConnection;
ClientConnection m_ClientConnection;

View File

@ -103,7 +103,7 @@
</property>
<layout class="QHBoxLayout" name="horizontalLayout_8">
<item>
<widget class="QFrame" name="m_pGroupServer">
<widget class="QGroupBox" name="m_pGroupServer">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Preferred">
<horstretch>0</horstretch>
@ -268,7 +268,7 @@
</widget>
</item>
<item>
<widget class="QFrame" name="m_pGroupClient">
<widget class="QGroupBox" name="m_pGroupClient">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Preferred">
<horstretch>0</horstretch>
@ -599,6 +599,9 @@
</property>
</action>
<action name="m_pActionStartCore">
<property name="enabled">
<bool>false</bool>
</property>
<property name="text">
<string>&amp;Start</string>
</property>

View File

@ -24,18 +24,4 @@
#include <QtGui>
QSynergyApplication::QSynergyApplication(int &argc, char **argv)
: QApplication(argc, argv) {
// Setting the style to 'Fusion' seems to fix issues such as text being
// rendered as black on black. This may not be the style we want long-term
// but it does fix the style issues for now.
setStyle("Fusion");
}
void QSynergyApplication::commitData(const QSessionManager &) const {
foreach (QWidget *widget, topLevelWidgets()) {
MainWindow *mainWindow = qobject_cast<MainWindow *>(widget);
if (mainWindow)
mainWindow->saveSettings();
}
}
: QApplication(argc, argv) {}

View File

@ -26,6 +26,4 @@ class QSynergyApplication : public QApplication {
public:
QSynergyApplication(int &argc, char **argv);
~QSynergyApplication() override = default;
void commitData(const QSessionManager &manager) const;
};

View File

@ -18,7 +18,7 @@
#include "ScreenSettingsDialog.h"
#include "Screen.h"
#include "gui/constants.h"
#include "gui/styles.h"
#include "validators/AliasValidator.h"
#include "validators/ScreenNameValidator.h"
#include "validators/ValidationError.h"

View File

@ -21,8 +21,6 @@
#include "AddClientDialog.h"
#include "Hotkey.h"
#include "MainWindow.h"
#include "gui/ConfigScopes.h"
#include "gui/constants.h"
#include <QAbstractButton>
#include <QMessageBox>
@ -44,23 +42,14 @@ static const struct {
const int serverDefaultIndex = 7;
ServerConfig::ServerConfig(
int numColumns, int numRows, AppConfig *appConfig, MainWindow *mainWindow)
: m_pAppConfig(appConfig),
m_pMainWindow(mainWindow),
m_Screens(numColumns),
m_NumColumns(numColumns),
m_NumRows(numRows),
AppConfig &appConfig, MainWindow &mainWindow, int columns, int rows)
: m_pAppConfig(&appConfig),
m_pMainWindow(&mainWindow),
m_Screens(columns),
m_Columns(columns),
m_Rows(rows),
m_ClipboardSharingSize(defaultClipboardSharingSize()) {
appConfig->scopes().registerReceiver(this);
}
ServerConfig::~ServerConfig() {
try {
ServerConfig::saveSettings();
} catch (const std::exception &e) {
qDebug() << e.what();
m_pMainWindow->appendLogError(e.what());
}
recall();
}
bool ServerConfig::save(const QString &fileName) const {
@ -75,8 +64,8 @@ bool ServerConfig::save(const QString &fileName) const {
}
bool ServerConfig::operator==(const ServerConfig &sc) const {
return m_Screens == sc.m_Screens && m_NumColumns == sc.m_NumColumns &&
m_NumRows == sc.m_NumRows && m_HasHeartbeat == sc.m_HasHeartbeat &&
return m_Screens == sc.m_Screens && m_Columns == sc.m_Columns &&
m_Rows == sc.m_Rows && m_HasHeartbeat == sc.m_HasHeartbeat &&
m_Heartbeat == sc.m_Heartbeat &&
m_RelativeMouseMoves == sc.m_RelativeMouseMoves &&
m_Win32KeepForeground == sc.m_Win32KeepForeground &&
@ -99,7 +88,7 @@ void ServerConfig::save(QFile &file) const {
outStream << *this;
}
void ServerConfig::init() {
void ServerConfig::setupScreens() {
switchCorners().clear();
screens().clear();
hotkeys().clear();
@ -114,7 +103,9 @@ void ServerConfig::init() {
addScreen(Screen());
}
void ServerConfig::saveSettings() {
void ServerConfig::commit() {
qDebug("committing server config");
settings().beginGroup("internalConfig");
settings().remove("");
@ -163,7 +154,9 @@ void ServerConfig::saveSettings() {
settings().endGroup();
}
void ServerConfig::loadSettings() {
void ServerConfig::recall() {
qDebug("recalling server config");
settings().beginGroup("internalConfig");
setNumColumns(settings().value("numColumns", 5).toInt());
@ -171,7 +164,7 @@ void ServerConfig::loadSettings() {
// we need to know the number of columns and rows before we can set up
// ourselves
init();
setupScreens();
haveHeartbeat(settings().value("hasHeartbeat", false).toBool());
setHeartbeat(settings().value("heartbeat", 5000).toInt());
@ -398,7 +391,6 @@ int ServerConfig::autoAddScreen(const QString name) {
return kAutoAddScreenManualClient;
}
saveSettings();
return kAutoAddScreenOk;
}
@ -410,7 +402,6 @@ void ServerConfig::updateServerName() {
for (auto &screen : screens()) {
if (screen.isServer()) {
screen.setName(m_pAppConfig->screenName());
saveSettings();
break;
}
}
@ -553,5 +544,5 @@ QString ServerConfig::getClientAddress() const {
}
QSettings &ServerConfig::settings() {
return *m_pAppConfig->scopes().currentSettings();
return *m_pAppConfig->scopes().activeSettings();
}

View File

@ -21,10 +21,12 @@
#include "Hotkey.h"
#include "ScreenConfig.h"
#include "ScreenList.h"
#include "gui/CommonConfig.h"
#include <QList>
const auto kDefaultColumns = 5;
const auto kDefaultRows = 3;
class QTextStream;
class QSettings;
class QString;
@ -33,7 +35,7 @@ class ServerConfigDialog;
class MainWindow;
class AppConfig;
class ServerConfig : public ScreenConfig, public synergy::gui::CommonConfig {
class ServerConfig : public ScreenConfig {
friend class ServerConfigDialog;
friend class ServerConnection;
friend QTextStream &
@ -41,15 +43,15 @@ class ServerConfig : public ScreenConfig, public synergy::gui::CommonConfig {
public:
ServerConfig(
int numColumns, int numRows, AppConfig *appConfig,
MainWindow *mainWindow);
~ServerConfig() override;
AppConfig &appConfig, MainWindow &mainWindow,
int columns = kDefaultColumns, int rows = kDefaultRows);
~ServerConfig() = default;
bool operator==(const ServerConfig &sc) const;
const ScreenList &screens() const { return m_Screens; }
int numColumns() const { return m_NumColumns; }
int numRows() const { return m_NumRows; }
int numColumns() const { return m_Columns; }
int numRows() const { return m_Rows; }
bool hasHeartbeat() const { return m_HasHeartbeat; }
int heartbeat() const { return m_Heartbeat; }
bool relativeMouseMoves() const { return m_RelativeMouseMoves; }
@ -68,8 +70,7 @@ public:
size_t clipboardSharingSize() const { return m_ClipboardSharingSize; }
static size_t defaultClipboardSharingSize();
void saveSettings() override;
void loadSettings() override;
void commit();
bool save(const QString &fileName) const;
void save(QFile &file) const;
int numScreens() const;
@ -84,13 +85,15 @@ public:
QString getClientAddress() const;
void setClientAddress(const QString &address);
protected:
private:
void recall();
void setupScreens();
QSettings &settings();
ScreenList &screens() { return m_Screens; }
void setScreens(const ScreenList &screens) { m_Screens = screens; }
void addScreen(const Screen &screen) { m_Screens.append(screen); }
void setNumColumns(int n) { m_NumColumns = n; }
void setNumRows(int n) { m_NumRows = n; }
void setNumColumns(int n) { m_Columns = n; }
void setNumRows(int n) { m_Rows = n; }
void haveHeartbeat(bool on) { m_HasHeartbeat = on; }
void setHeartbeat(int val) { m_Heartbeat = val; }
void setRelativeMouseMoves(bool on) { m_RelativeMouseMoves = on; }
@ -109,11 +112,7 @@ protected:
size_t setClipboardSharingSize(size_t size);
QList<bool> &switchCorners() { return m_SwitchCorners; }
HotkeyList &hotkeys() { return m_Hotkeys; }
void init();
int adjacentScreenIndex(int idx, int deltaColumn, int deltaRow) const;
private:
bool findScreenName(const QString &name, int &index);
bool fixNoServer(const QString &name, int &index);
int showAddClientDialog(const QString &clientName);
@ -139,8 +138,8 @@ private:
AppConfig *m_pAppConfig;
MainWindow *m_pMainWindow;
ScreenList m_Screens;
int m_NumColumns;
int m_NumRows;
int m_Columns;
int m_Rows;
size_t m_ClipboardSharingSize;
};

View File

@ -332,7 +332,7 @@
</widget>
<widget class="QWidget" name="m_pTabAdvanced">
<attribute name="title">
<string>Advanced server settings</string>
<string>Advanced</string>
</attribute>
<layout class="QGridLayout">
<property name="leftMargin">
@ -839,289 +839,284 @@
</sizepolicy>
</property>
<attribute name="title">
<string>Advanced config</string>
<string>Config file</string>
</attribute>
<layout class="QVBoxLayout" name="verticalLayout_2">
<property name="spacing">
<number>15</number>
</property>
<property name="leftMargin">
<number>120</number>
</property>
<property name="topMargin">
<number>30</number>
</property>
<property name="rightMargin">
<number>120</number>
</property>
<item alignment="Qt::AlignHCenter">
<widget class="QLabel" name="label_5">
<property name="font">
<font>
<pointsize>18</pointsize>
<bold>true</bold>
</font>
</property>
<property name="text">
<string>Core server config file</string>
</property>
<property name="textFormat">
<enum>Qt::PlainText</enum>
</property>
</widget>
</item>
<item>
<layout class="QVBoxLayout" name="verticalLayout">
<widget class="QLabel" name="label_6">
<property name="text">
<string>Use a Core server config file to create complex screen layouts that are not possible with the simple grid-based screen layout editor.
[How to write a config file](https://symless.com/synergy/help/creating-text-config-files)
Enabling this setting will disable the server config GUI.</string>
</property>
<property name="textFormat">
<enum>Qt::MarkdownText</enum>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<spacer name="verticalSpacer_4">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeType">
<enum>QSizePolicy::Maximum</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>30</height>
</size>
</property>
</spacer>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_2">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<item>
<spacer name="verticalSpacer_2">
<spacer name="horizontalSpacer_2">
<property name="orientation">
<enum>Qt::Vertical</enum>
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeType">
<enum>QSizePolicy::Expanding</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QLabel" name="label_5">
<property name="text">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p style=&quot;text-align: center; font-family: Arial; font-size: 18px; line-height: 25px;&quot;&gt;Config override&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
<widget class="QCheckBox" name="m_pCheckBoxUseExternalConfig">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="textFormat">
<enum>Qt::RichText</enum>
<property name="minimumSize">
<size>
<width>300</width>
<height>0</height>
</size>
</property>
<property name="text">
<string>Use Core server config file</string>
</property>
</widget>
</item>
<item>
<spacer name="verticalSpacer_3">
<spacer name="horizontalSpacer_3">
<property name="orientation">
<enum>Qt::Vertical</enum>
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeType">
<enum>QSizePolicy::Minimum</enum>
<enum>QSizePolicy::Expanding</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>5</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QLabel" name="label_6">
<property name="text">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p style=&quot;text-align: center; font-size: 13px; font-family: Arial; line-hegiht: 18px;&quot;&gt;If you have a config file and want to override all setttings with this file.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="textFormat">
<enum>Qt::RichText</enum>
</property>
</widget>
</item>
<item>
<spacer name="verticalSpacer_4">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeType">
<enum>QSizePolicy::Maximum</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>30</height>
</size>
</property>
</spacer>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_2">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<item>
<spacer name="horizontalSpacer_2">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeType">
<enum>QSizePolicy::Expanding</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QCheckBox" name="m_pCheckBoxUseExternalConfig">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>300</width>
<height>0</height>
</size>
</property>
<property name="text">
<string>Use configuration file</string>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_3">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeType">
<enum>QSizePolicy::Expanding</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_3">
<property name="leftMargin">
<number>40</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<item>
<spacer name="horizontalSpacer_4">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeType">
<enum>QSizePolicy::Expanding</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QLabel" name="m_pLabelConfigFile">
<property name="enabled">
<bool>false</bool>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>96</width>
<height>0</height>
</size>
</property>
<property name="text">
<string>Config file path</string>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_6">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeType">
<enum>QSizePolicy::Fixed</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>10</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QLineEdit" name="m_pEditConfigFile">
<property name="enabled">
<bool>false</bool>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>180</width>
<height>0</height>
</size>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="m_pButtonBrowseConfigFile">
<property name="enabled">
<bool>false</bool>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>0</width>
<height>0</height>
</size>
</property>
<property name="cursor">
<cursorShape>PointingHandCursor</cursorShape>
</property>
<property name="text">
<string/>
</property>
<property name="icon">
<iconset resource="../res/Synergy.qrc">
<normaloff>:/res/icons/64x64/folder.png</normaloff>:/res/icons/64x64/folder.png</iconset>
</property>
<property name="iconSize">
<size>
<width>20</width>
<height>13</height>
</size>
</property>
<property name="flat">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_5">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeType">
<enum>QSizePolicy::Expanding</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
<item>
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_3">
<property name="leftMargin">
<number>40</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<item>
<spacer name="horizontalSpacer_4">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeType">
<enum>QSizePolicy::Expanding</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QLabel" name="m_pLabelConfigFile">
<property name="enabled">
<bool>false</bool>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>96</width>
<height>0</height>
</size>
</property>
<property name="text">
<string>Config file path</string>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_6">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeType">
<enum>QSizePolicy::Fixed</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>10</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QLineEdit" name="m_pEditConfigFile">
<property name="enabled">
<bool>false</bool>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>180</width>
<height>0</height>
</size>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="m_pButtonBrowseConfigFile">
<property name="enabled">
<bool>false</bool>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>0</width>
<height>0</height>
</size>
</property>
<property name="cursor">
<cursorShape>PointingHandCursor</cursorShape>
</property>
<property name="text">
<string/>
</property>
<property name="icon">
<iconset resource="../res/Synergy.qrc">
<normaloff>:/res/icons/64x64/folder.png</normaloff>:/res/icons/64x64/folder.png</iconset>
</property>
<property name="iconSize">
<size>
<width>20</width>
<height>13</height>
</size>
</property>
<property name="flat">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_5">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeType">
<enum>QSizePolicy::Expanding</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
<item>
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
</layout>
</widget>
</widget>

View File

@ -69,10 +69,10 @@ void ServerConnection::addClient(const QString &clientName) {
}
void ServerConnection::configureClient(const QString &clientName) {
ServerConfigDialog dlg(
ServerConfigDialog dialog(
&m_parent, m_parent.serverConfig(), m_parent.appConfig());
if (dlg.addClient(clientName) && dlg.exec() == QDialog::Accepted) {
if (dialog.addClient(clientName) && dialog.exec() == QDialog::Accepted) {
m_parent.restartCore();
}
}

View File

@ -53,7 +53,7 @@ SettingsDialog::SettingsDialog(
m_pMainWindow = dynamic_cast<MainWindow *>(parent);
loadFromConfig();
m_isSystemAtStart = appConfig().isSystemScoped();
m_wasOriginallySystemScope = appConfig().isActiveScopeSystem();
updateControlsEnabled();
const auto &serveConfig = m_pMainWindow->serverConfig();
@ -86,18 +86,17 @@ void SettingsDialog::accept() {
appConfig().setTlsEnabled(m_pCheckBoxEnableCrypto->isChecked());
appConfig().setLanguageSync(m_pCheckBoxLanguageSync->isChecked());
appConfig().setInvertScrollDirection(m_pCheckBoxScrollDirection->isChecked());
appConfig().setServiceEnabled(m_pCheckBoxServiceEnabled->isChecked());
appConfig().setEnableService(m_pCheckBoxServiceEnabled->isChecked());
appConfig().setCloseToTray(m_pCheckBoxCloseToTray->isChecked());
appConfig().setInvertConnection(m_pInvertConnection->isChecked());
appConfig().saveSettings();
QDialog::accept();
}
void SettingsDialog::reject() {
// We should restore scope at start if the user rejects changes.
if (appConfig().isSystemScoped() != m_isSystemAtStart) {
appConfig().setLoadFromSystemScope(m_isSystemAtStart);
// restore original system scope value on reject.
if (appConfig().isActiveScopeSystem() != m_wasOriginallySystemScope) {
appConfig().setLoadFromSystemScope(m_wasOriginallySystemScope);
}
QDialog::reject();
@ -117,10 +116,10 @@ void SettingsDialog::loadFromConfig() {
m_pCheckBoxEnableCrypto->setChecked(m_appConfig.tlsEnabled());
m_pCheckBoxLanguageSync->setChecked(m_appConfig.languageSync());
m_pCheckBoxScrollDirection->setChecked(m_appConfig.invertScrollDirection());
m_pCheckBoxServiceEnabled->setChecked(m_appConfig.serviceEnabled());
m_pCheckBoxServiceEnabled->setChecked(m_appConfig.enableService());
m_pCheckBoxCloseToTray->setChecked(m_appConfig.closeToTray());
if (m_appConfig.isSystemScoped()) {
if (m_appConfig.isActiveScopeSystem()) {
m_pRadioSystemScope->setChecked(true);
} else {
m_pRadioUserScope->setChecked(true);
@ -147,14 +146,15 @@ void SettingsDialog::updateTlsControls() {
}
void SettingsDialog::updateTlsControlsEnabled() {
bool writable = appConfig().isActiveScopeWritable();
auto clientMode = appConfig().clientGroupChecked();
auto tlsAvailable = m_tlsUtility.isAvailableAndEnabled();
auto tlsChecked = m_pCheckBoxEnableCrypto->isChecked();
auto enabled = !clientMode && tlsAvailable && tlsChecked;
auto enabled = writable && !clientMode && tlsAvailable && tlsChecked;
qDebug(
"tls enabled=%d, client=%d, available=%d, checked=%d", enabled,
clientMode, tlsAvailable, tlsChecked);
"tls enabled=%d, writable=%d, client=%d, available=%d, checked=%d",
enabled, writable, clientMode, tlsAvailable, tlsChecked);
m_pLabelKeyLength->setEnabled(enabled);
m_pComboBoxKeyLength->setEnabled(enabled);
@ -165,7 +165,7 @@ void SettingsDialog::updateTlsControlsEnabled() {
}
bool SettingsDialog::isClientMode() const {
return (m_pMainWindow->coreMode() == MainWindow::CoreMode::Client);
return m_pMainWindow->coreMode() == MainWindow::CoreMode::Client;
}
void SettingsDialog::on_m_pCheckBoxLogToFile_stateChanged(int i) {
@ -254,7 +254,7 @@ void SettingsDialog::updateKeyLengthOnFile(const QString &path) {
}
void SettingsDialog::updateControlsEnabled() {
bool writable = appConfig().isWritable();
bool writable = appConfig().isActiveScopeWritable();
m_pLineEditScreenName->setEnabled(writable);
m_pSpinBoxPort->setEnabled(writable);

View File

@ -76,7 +76,7 @@ private:
/// @brief Stores settings scope at start of settings dialog
/// This is neccessary to restore state if user changes
/// the scope and doesn't save changes
bool m_isSystemAtStart = false;
bool m_wasOriginallySystemScope = false;
private slots:
void on_m_pCheckBoxEnableCrypto_clicked(bool checked);

View File

@ -1,6 +1,6 @@
/*
* synergy -- mouse and keyboard sharing utility
* Copyright (C) 2012-2016 Symless Ltd.
* Copyright (C) 2012 Symless Ltd.
*
* This package is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
@ -17,18 +17,16 @@
#include "SetupWizard.h"
#include "MainWindow.h"
#include "gui/constants.h"
#include "gui/styles.h"
#include "validators/ScreenNameValidator.h"
#include "validators/ValidationError.h"
SetupWizard::SetupWizard(MainWindow &mainWindow) : m_mainWindow(mainWindow) {
SetupWizard::SetupWizard(AppConfig &appConfig) : m_appConfig(appConfig) {
setupUi(this);
m_pLabelError->setStyleSheet(kStyleErrorActiveLabel);
m_pLineEditName->setText(m_mainWindow.appConfig().screenName());
m_pLineEditName->setText(appConfig.screenName());
m_pLineEditName->setValidator(new validators::ScreenNameValidator(
m_pLineEditName, new validators::ValidationError(this, m_pLabelError)));
@ -39,13 +37,8 @@ SetupWizard::SetupWizard(MainWindow &mainWindow) : m_mainWindow(mainWindow) {
}
void SetupWizard::accept() {
AppConfig &appConfig = m_mainWindow.appConfig();
appConfig.setWizardHasRun();
appConfig.setScreenName(m_pLineEditName->text());
appConfig.saveSettings();
m_mainWindow.open();
m_appConfig.setWizardHasRun();
m_appConfig.setScreenName(m_pLineEditName->text());
QDialog::accept();
}

View File

@ -19,6 +19,8 @@
#include "ui_SetupWizardBase.h"
#include "gui/AppConfig.h"
#include <QDialog>
#include <QObject>
@ -28,14 +30,14 @@ class SetupWizard : public QDialog, public Ui::SetupWizardBase {
Q_OBJECT
public:
explicit SetupWizard(MainWindow &mainWindow);
explicit SetupWizard(AppConfig &appConfig);
protected:
void accept();
void reject();
private:
MainWindow &m_mainWindow;
AppConfig &m_appConfig;
public slots:
void onLineEditNameChanged(const QString &error);

View File

@ -6,8 +6,8 @@
<rect>
<x>0</x>
<y>0</y>
<width>772</width>
<height>634</height>
<width>764</width>
<height>612</height>
</rect>
</property>
<property name="sizePolicy">
@ -36,10 +36,10 @@
<number>20</number>
</property>
<property name="leftMargin">
<number>100</number>
<number>150</number>
</property>
<property name="rightMargin">
<number>100</number>
<number>150</number>
</property>
<item>
<spacer name="verticalSpacer_2">
@ -54,7 +54,7 @@
</property>
</spacer>
</item>
<item alignment="Qt::AlignHCenter">
<item alignment="Qt::AlignHCenter|Qt::AlignTop">
<widget class="QLabel" name="m_pLabelImage">
<property name="text">
<string/>
@ -63,11 +63,11 @@
<pixmap resource="../res/Synergy.qrc">:/res/image/welcome.png</pixmap>
</property>
<property name="margin">
<number>30</number>
<number>1</number>
</property>
</widget>
</item>
<item alignment="Qt::AlignHCenter">
<item alignment="Qt::AlignHCenter|Qt::AlignTop">
<widget class="QLabel" name="m_pLabelTitle">
<property name="font">
<font>
@ -86,6 +86,15 @@
</item>
<item>
<layout class="QFormLayout" name="formLayout">
<property name="leftMargin">
<number>50</number>
</property>
<property name="topMargin">
<number>30</number>
</property>
<property name="rightMargin">
<number>50</number>
</property>
<item row="0" column="0">
<widget class="QLabel" name="m_pLabelName">
<property name="text">
@ -121,7 +130,7 @@
</item>
</layout>
</item>
<item alignment="Qt::AlignHCenter">
<item alignment="Qt::AlignHCenter|Qt::AlignTop">
<widget class="QLabel" name="m_pHelpList">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Preferred">

View File

@ -1,6 +1,6 @@
/*
* synergy -- mouse and keyboard sharing utility
* Copyright (C) 2016 Symless Ltd.
* Copyright (C) 2021 Symless Ltd.
*
* This package is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
@ -33,9 +33,7 @@ static const std::vector<const char *> blockerText = {
"Please switch to Xorg if you wish to continue using Synergy today.",
};
SetupWizardBlocker::SetupWizardBlocker(
MainWindow &mainWindow, qBlockerType type)
: m_MainWindow(mainWindow) {
SetupWizardBlocker::SetupWizardBlocker(BlockerType type) {
setupUi(this);
m_pLabelTitle->setText(blockerTitels[static_cast<int>(type)]);

View File

@ -1,6 +1,6 @@
/*
* synergy -- mouse and keyboard sharing utility
* Copyright (C) 2012-2016 Symless Ltd.
* Copyright (C) 2021 Symless Ltd.
*
* This package is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
@ -16,7 +16,9 @@
*/
#pragma once
#include "ui_SetupWizardBlocker.h"
#include <QDialog>
class MainWindow;
@ -25,13 +27,10 @@ class SetupWizardBlocker : public QDialog, public Ui::SetupWizardBlocker {
Q_OBJECT
public:
enum class qBlockerType { waylandDetected };
explicit SetupWizardBlocker(MainWindow &mainWindow, qBlockerType type);
enum class BlockerType { Wayland };
explicit SetupWizardBlocker(BlockerType type);
protected:
void onlineSupport();
void cancel();
private:
MainWindow &m_MainWindow;
};

View File

@ -21,9 +21,16 @@
#include "SetupWizard.h"
#include "SetupWizardBlocker.h"
#include "gui/AppConfig.h"
#include "gui/ConfigScopes.h"
#include "gui/constants.h"
#include "gui/dotenv.h"
#include "gui/messages.h"
#include "gui/version.h"
#include <QApplication>
#include <QDebug>
#include <QMessageBox>
#include <QObject>
#include <QtCore>
#include <QtGui>
@ -46,19 +53,23 @@ bool checkMacAssistiveDevices();
#endif
int main(int argc, char *argv[]) {
#ifdef Q_OS_DARWIN
/* Workaround for QTBUG-40332 - "High ping when QNetworkAccessManager is
* instantiated" */
::setenv("QT_BEARER_POLL_TIMEOUT", "-1", 1);
#endif
dotenv(".env");
QCoreApplication::setOrganizationName("Synergy");
QCoreApplication::setOrganizationDomain("http://symless.com/");
QCoreApplication::setApplicationName("Synergy");
QCoreApplication::setOrganizationName(kAppName);
QCoreApplication::setApplicationName(kAppName);
QCoreApplication::setOrganizationDomain(kAppDomain);
QSynergyApplication app(argc, argv);
qInstallMessageHandler(synergy::gui::messages::messageHandler);
qInfo("Synergy v%s", synergy::gui::version().toUtf8().constData());
dotenv(".env");
#if defined(Q_OS_MAC)
@ -75,31 +86,41 @@ int main(int argc, char *argv[]) {
}
#endif
AppConfig appConfig;
qRegisterMetaType<Edition>("Edition");
MainWindow mainWindow(appConfig);
ConfigScopes configScopes;
AppConfig appConfig(configScopes);
QObject::connect(
&configScopes, &ConfigScopes::saving, &appConfig,
[&appConfig]() { appConfig.commit(); }, Qt::DirectConnection);
std::unique_ptr<SetupWizardBlocker> setupBlocker;
if (qgetenv("XDG_SESSION_TYPE") == "wayland") {
SetupWizardBlocker blocked(SetupWizardBlocker::BlockerType::Wayland);
blocked.exec();
qInfo("wayland detected, exiting");
return 0;
}
if (appConfig.wizardShouldRun()) {
SetupWizard wizard(appConfig);
auto result = wizard.exec();
if (result != QDialog::Accepted) {
qInfo("wizard cancelled, exiting");
return 0;
}
configScopes.save();
}
MainWindow mainWindow(configScopes, appConfig);
QObject::connect(
&app, &QSynergyApplication::aboutToQuit, &mainWindow,
&MainWindow::onAppAboutToQuit);
std::unique_ptr<SetupWizardBlocker> setupBlocker;
if (qgetenv("XDG_SESSION_TYPE") == "wayland") {
setupBlocker.reset(new SetupWizardBlocker(
mainWindow, SetupWizardBlocker::qBlockerType::waylandDetected));
setupBlocker->show();
return QApplication::exec();
}
std::unique_ptr<SetupWizard> setupWizard;
if (appConfig.wizardShouldRun()) {
setupWizard.reset(new SetupWizard(mainWindow));
setupWizard->show();
} else {
mainWindow.open();
}
mainWindow.open();
return QSynergyApplication::exec();
}

View File

@ -17,7 +17,7 @@
#include "LineEditValidator.h"
#include "gui/constants.h"
#include "gui/styles.h"
#include <qvalidator.h>
namespace validators {

View File

@ -17,7 +17,7 @@
#include "ValidationError.h"
#include "gui/constants.h"
#include "gui/styles.h"
namespace validators {

View File

@ -49,8 +49,6 @@ static const int s_family[] = {
static const int s_type[] = {SOCK_DGRAM, SOCK_STREAM};
ArchNetworkBSD::Deps ArchNetworkBSD::s_deps;
#if !HAVE_INET_ATON
// parse dotted quad addresses. we don't bother with the weird BSD'ism
// of handling octal and hex and partial forms.
@ -274,13 +272,13 @@ int ArchNetworkBSD::pollSocket(PollEntry pe[], int num, double timeout) {
// return if nothing to do
if (num == 0) {
if (timeout > 0.0) {
m_deps.sleep(timeout);
m_pDeps->sleep(timeout);
}
return 0;
}
// allocate space for translated query
auto pfdPtr = m_deps.makePollFD(1 + num);
auto pfdPtr = m_pDeps->makePollFD(1 + num);
auto *pfd = pfdPtr.get();
// translate query
@ -308,14 +306,14 @@ int ArchNetworkBSD::pollSocket(PollEntry pe[], int num, double timeout) {
int t = (timeout < 0.0) ? -1 : static_cast<int>(1000.0 * timeout);
// do the poll
n = m_deps.poll(pfd, n, t);
n = m_pDeps->poll(pfd, n, t);
// reset the unblock pipe
if (n > 0 && unblockPipe != nullptr && (pfd[num].revents & POLLIN) != 0) {
// the unblock event was signalled. flush the pipe.
char dummy[100];
do {
m_deps.read(unblockPipe[0], dummy, sizeof(dummy));
m_pDeps->read(unblockPipe[0], dummy, sizeof(dummy));
} while (errno != EAGAIN);
// don't count this unblock pipe in return value
@ -326,7 +324,7 @@ int ArchNetworkBSD::pollSocket(PollEntry pe[], int num, double timeout) {
if (n == -1) {
if (errno == EINTR) {
// interrupted system call
m_deps.testCancelThread();
m_pDeps->testCancelThread();
return 0;
}
throwError(errno);

View File

@ -20,6 +20,7 @@
#include "arch/IArchMultithread.h"
#include "arch/IArchNetwork.h"
#include <memory>
#if HAVE_SYS_TYPES_H
@ -69,6 +70,7 @@ public:
//! Berkeley (BSD) sockets implementation of IArchNetwork
class ArchNetworkBSD : public IArchNetwork {
public:
struct Deps {
virtual ~Deps() = default;
@ -79,8 +81,8 @@ public:
virtual void testCancelThread();
};
explicit ArchNetworkBSD() : m_deps(s_deps) {}
explicit ArchNetworkBSD(Deps &deps) : m_deps(deps) {}
explicit ArchNetworkBSD(std::shared_ptr<Deps> deps = std::make_shared<Deps>())
: m_pDeps(deps) {}
ArchNetworkBSD(ArchNetworkBSD const &) = delete;
ArchNetworkBSD(ArchNetworkBSD &&) = delete;
~ArchNetworkBSD() override;
@ -128,7 +130,6 @@ private:
void throwNameError(int);
private:
static Deps s_deps;
Deps &m_deps;
std::shared_ptr<Deps> m_pDeps;
ArchMutex m_mutex{};
};

View File

@ -86,146 +86,156 @@ const char *const AppConfig::m_SettingsName[] = {
"initiateConnectionFromServer", // kInvertConnection
"", // 35 = clientHostMode, obsolete
"", // 36 = serverClientMode, obsolete
"serviceEnabled",
"enableService",
"closeToTray",
"mainWindowSize",
"mainWindowPosition",
"showDevThanks",
"showCloseReminder",
};
static const char *logLevelNames[] = {"INFO", "DEBUG", "DEBUG1", "DEBUG2"};
AppConfig::Deps AppConfig::s_Deps;
AppConfig::AppConfig(Deps &deps) : m_Deps(deps), m_ScreenName(deps.hostname()) {
m_Deps.scopes().registerReceiver(this);
AppConfig::AppConfig(
synergy::gui::IConfigScopes &scopes, std::shared_ptr<Deps> deps)
: m_scopes(scopes),
m_pDeps(deps),
m_ScreenName(deps->hostname()) {
determineScope();
recall();
}
void AppConfig::loadAllScopes() {
m_Deps.scopes().loadAll();
// User settings exist and the load from system scope variable is true
if (m_Deps.scopes().hasSetting(
settingName(Setting::kLoadSystemSettings),
ConfigScopes::Scope::User)) {
setLoadFromSystemScope(m_LoadFromSystemScope);
}
// If user setting don't exist but system ones do, load the system settings
else if (m_Deps.scopes().hasSetting(
settingName(Setting::kScreenName),
ConfigScopes::Scope::System)) {
setLoadFromSystemScope(true);
}
}
void AppConfig::loadSettings() {
void AppConfig::recall() {
using enum AppConfig::Setting;
qDebug("loading settings");
qDebug("recalling app config");
loadCommonSettings();
loadScopeSettings();
loadSerialKey();
loadElevateMode();
emit loaded();
recallFromAllScopes();
recallFromCurrentScope();
}
void AppConfig::loadCommonSettings() {
void AppConfig::recallFromAllScopes() {
using enum Setting;
m_WizardLastRun = loadCommonSetting(kWizardLastRun, m_WizardLastRun).toInt();
m_WizardLastRun = findInAllScopes(kWizardLastRun, m_WizardLastRun).toInt();
m_LoadFromSystemScope =
loadCommonSetting(kLoadSystemSettings, m_LoadFromSystemScope).toBool();
findInAllScopes(kLoadSystemSettings, m_LoadFromSystemScope).toBool();
m_licenseNextCheck =
loadCommonSetting(kLicenseNextCheck, m_licenseNextCheck).toULongLong();
findInAllScopes(kLicenseNextCheck, m_licenseNextCheck).toULongLong();
}
void AppConfig::loadScopeSettings() {
void AppConfig::recallFromCurrentScope() {
using enum Setting;
loadScreenName();
recallScreenName();
recallSerialKey();
recallElevateMode();
m_Port = loadSetting(kPort, m_Port).toInt();
m_Interface = loadSetting(kInterface, m_Interface).toString();
m_LogLevel = loadSetting(kLogLevel, m_LogLevel).toInt();
m_LogToFile = loadSetting(kLogToFile, m_LogToFile).toBool();
m_LogFilename = loadSetting(kLogFilename, m_LogFilename).toString();
m_StartedBefore = loadSetting(kStartedBefore, m_StartedBefore).toBool();
m_AutoHide = loadSetting(kAutoHide, m_AutoHide).toBool();
m_LastVersion = loadSetting(kLastVersion, m_LastVersion).toString();
m_Port = getFromCurrentScope(kPort, m_Port).toInt();
m_Interface = getFromCurrentScope(kInterface, m_Interface).toString();
m_LogLevel = getFromCurrentScope(kLogLevel, m_LogLevel).toInt();
m_LogToFile = getFromCurrentScope(kLogToFile, m_LogToFile).toBool();
m_LogFilename = getFromCurrentScope(kLogFilename, m_LogFilename).toString();
m_StartedBefore =
getFromCurrentScope(kStartedBefore, m_StartedBefore).toBool();
m_AutoHide = getFromCurrentScope(kAutoHide, m_AutoHide).toBool();
m_LastVersion = getFromCurrentScope(kLastVersion, m_LastVersion).toString();
m_ActivationHasRun =
loadSetting(kActivationHasRun, m_ActivationHasRun).toBool();
getFromCurrentScope(kActivationHasRun, m_ActivationHasRun).toBool();
m_ServerGroupChecked =
loadSetting(kServerGroupChecked, m_ServerGroupChecked).toBool();
getFromCurrentScope(kServerGroupChecked, m_ServerGroupChecked).toBool();
m_UseExternalConfig =
loadSetting(kUseExternalConfig, m_UseExternalConfig).toBool();
m_ConfigFile = loadSetting(kConfigFile, m_ConfigFile).toString();
getFromCurrentScope(kUseExternalConfig, m_UseExternalConfig).toBool();
m_ConfigFile = getFromCurrentScope(kConfigFile, m_ConfigFile).toString();
m_UseInternalConfig =
loadSetting(kUseInternalConfig, m_UseInternalConfig).toBool();
getFromCurrentScope(kUseInternalConfig, m_UseInternalConfig).toBool();
m_ClientGroupChecked =
loadSetting(kClientGroupChecked, m_ClientGroupChecked).toBool();
m_ServerHostname = loadSetting(kServerHostname, m_ServerHostname).toString();
m_PreventSleep = loadSetting(kPreventSleep, m_PreventSleep).toBool();
m_LanguageSync = loadSetting(kLanguageSync, m_LanguageSync).toBool();
getFromCurrentScope(kClientGroupChecked, m_ClientGroupChecked).toBool();
m_ServerHostname =
getFromCurrentScope(kServerHostname, m_ServerHostname).toString();
m_PreventSleep = getFromCurrentScope(kPreventSleep, m_PreventSleep).toBool();
m_LanguageSync = getFromCurrentScope(kLanguageSync, m_LanguageSync).toBool();
m_InvertScrollDirection =
loadSetting(kInvertScrollDirection, m_InvertScrollDirection).toBool();
getFromCurrentScope(kInvertScrollDirection, m_InvertScrollDirection)
.toBool();
m_InvertConnection =
loadSetting(kInvertConnection, m_InvertConnection).toBool();
m_ServiceEnabled = loadSetting(kServiceEnabled, m_ServiceEnabled).toBool();
m_CloseToTray = loadSetting(kCloseToTray, m_CloseToTray).toBool();
m_TlsEnabled = loadSetting(kTlsEnabled, m_TlsEnabled).toBool();
m_TlsCertPath = loadSetting(kTlsCertPath, m_TlsCertPath).toString();
m_TlsKeyLength = loadSetting(kTlsKeyLength, m_TlsKeyLength).toString();
m_MainWindowPosition = loadOptional<QPoint>(
kMainWindowPosition, [](QVariant v) { return v.toPoint(); });
m_MainWindowSize = loadOptional<QSize>(
kMainWindowSize, [](QVariant v) { return v.toSize(); });
getFromCurrentScope(kInvertConnection, m_InvertConnection).toBool();
m_EnableService =
getFromCurrentScope(kEnableService, m_EnableService).toBool();
m_CloseToTray = getFromCurrentScope(kCloseToTray, m_CloseToTray).toBool();
m_TlsEnabled = getFromCurrentScope(kTlsEnabled, m_TlsEnabled).toBool();
m_TlsCertPath = getFromCurrentScope(kTlsCertPath, m_TlsCertPath).toString();
m_TlsKeyLength =
getFromCurrentScope(kTlsKeyLength, m_TlsKeyLength).toString();
m_MainWindowPosition = getFromCurrentScope<QPoint>(
kMainWindowPosition, [](const QVariant &v) { return v.toPoint(); });
m_MainWindowSize = getFromCurrentScope<QSize>(
kMainWindowSize, [](const QVariant &v) { return v.toSize(); });
m_ShowDevThanks =
getFromCurrentScope(kShowDevThanks, m_ShowDevThanks).toBool();
m_ShowCloseReminder =
getFromCurrentScope(kShowCloseReminder, m_ShowCloseReminder).toBool();
}
void AppConfig::saveSettings() {
void AppConfig::recallScreenName() {
using enum Setting;
qDebug("saving settings");
const auto &screenName =
getFromCurrentScope(kScreenName, m_ScreenName).toString().trimmed();
setCommonSetting(kWizardLastRun, m_WizardLastRun);
setCommonSetting(kLoadSystemSettings, m_LoadFromSystemScope);
setCommonSetting(kClientGroupChecked, m_ClientGroupChecked);
setCommonSetting(kServerGroupChecked, m_ServerGroupChecked);
setCommonSetting(kLicenseNextCheck, m_licenseNextCheck);
if (isWritable()) {
setSetting(kScreenName, m_ScreenName);
setSetting(kPort, m_Port);
setSetting(kInterface, m_Interface);
setSetting(kLogLevel, m_LogLevel);
setSetting(kLogToFile, m_LogToFile);
setSetting(kLogFilename, m_LogFilename);
setSetting(kStartedBefore, m_StartedBefore);
setSetting(kElevateMode, static_cast<int>(m_ElevateMode));
setSetting(kElevateModeLegacy, m_ElevateMode == ElevateAlways);
setSetting(kTlsEnabled, m_TlsEnabled);
setSetting(kAutoHide, m_AutoHide);
setSetting(kSerialKey, m_SerialKey);
setSetting(kLastVersion, m_LastVersion);
setSetting(kActivationHasRun, m_ActivationHasRun);
setSetting(kUseExternalConfig, m_UseExternalConfig);
setSetting(kConfigFile, m_ConfigFile);
setSetting(kUseInternalConfig, m_UseInternalConfig);
setSetting(kServerHostname, m_ServerHostname);
setSetting(kPreventSleep, m_PreventSleep);
setSetting(kLanguageSync, m_LanguageSync);
setSetting(kInvertScrollDirection, m_InvertScrollDirection);
setSetting(kInvertConnection, m_InvertConnection);
setSetting(kServiceEnabled, m_ServiceEnabled);
setSetting(kCloseToTray, m_CloseToTray);
setOptional(kMainWindowSize, m_MainWindowSize);
setOptional(kMainWindowPosition, m_MainWindowPosition);
// for some reason, the screen name can be saved as an empty string
// in the config file. this is probably a bug. if this happens, then default
// back to the hostname.
if (screenName.isEmpty()) {
qWarning("screen name was empty in config, setting to hostname");
m_ScreenName = m_pDeps->hostname();
} else {
m_ScreenName = screenName;
}
}
setModified(false);
void AppConfig::commit() {
using enum Setting;
emit saved();
qDebug("comitting app config");
saveToAllScopes(kWizardLastRun, m_WizardLastRun);
saveToAllScopes(kLoadSystemSettings, m_LoadFromSystemScope);
saveToAllScopes(kClientGroupChecked, m_ClientGroupChecked);
saveToAllScopes(kServerGroupChecked, m_ServerGroupChecked);
saveToAllScopes(kLicenseNextCheck, m_licenseNextCheck);
if (isActiveScopeWritable()) {
setInCurrentScope(kScreenName, m_ScreenName);
setInCurrentScope(kPort, m_Port);
setInCurrentScope(kInterface, m_Interface);
setInCurrentScope(kLogLevel, m_LogLevel);
setInCurrentScope(kLogToFile, m_LogToFile);
setInCurrentScope(kLogFilename, m_LogFilename);
setInCurrentScope(kStartedBefore, m_StartedBefore);
setInCurrentScope(kElevateMode, static_cast<int>(m_ElevateMode));
setInCurrentScope(kElevateModeLegacy, m_ElevateMode == ElevateAlways);
setInCurrentScope(kTlsEnabled, m_TlsEnabled);
setInCurrentScope(kAutoHide, m_AutoHide);
setInCurrentScope(kSerialKey, m_SerialKey);
setInCurrentScope(kLastVersion, m_LastVersion);
setInCurrentScope(kActivationHasRun, m_ActivationHasRun);
setInCurrentScope(kUseExternalConfig, m_UseExternalConfig);
setInCurrentScope(kConfigFile, m_ConfigFile);
setInCurrentScope(kUseInternalConfig, m_UseInternalConfig);
setInCurrentScope(kServerHostname, m_ServerHostname);
setInCurrentScope(kPreventSleep, m_PreventSleep);
setInCurrentScope(kLanguageSync, m_LanguageSync);
setInCurrentScope(kInvertScrollDirection, m_InvertScrollDirection);
setInCurrentScope(kInvertConnection, m_InvertConnection);
setInCurrentScope(kEnableService, m_EnableService);
setInCurrentScope(kCloseToTray, m_CloseToTray);
setInCurrentScope(kShowDevThanks, m_ShowDevThanks);
setInCurrentScope(kShowCloseReminder, m_ShowCloseReminder);
setInCurrentScope(kMainWindowSize, m_MainWindowSize);
setInCurrentScope(kMainWindowPosition, m_MainWindowPosition);
}
if (m_TlsChanged) {
m_TlsChanged = false;
@ -233,37 +243,45 @@ void AppConfig::saveSettings() {
}
}
void AppConfig::loadScreenName() {
using enum Setting;
void AppConfig::determineScope() {
const auto &screenName =
loadSetting(kScreenName, m_ScreenName).toString().trimmed();
qDebug("determining config scope");
// for some reason, the screen name can be saved as an empty string
// in the config file. this is probably a bug. if this happens, then default
// back to the hostname.
if (screenName.isEmpty()) {
qWarning("screen name was empty in config, setting to hostname");
m_ScreenName = m_Deps.hostname();
} else {
m_ScreenName = screenName;
// first, try to determine if the system scope should be used according to the
// user scope...
if (m_scopes.scopeContains(
settingName(Setting::kLoadSystemSettings),
ConfigScopes::Scope::User)) {
auto loadFromSystemScope =
m_scopes
.getFromScope(
settingName(Setting::kLoadSystemSettings),
m_LoadFromSystemScope, ConfigScopes::Scope::User)
.toBool();
setLoadFromSystemScope(loadFromSystemScope);
}
// ...failing that, check the system scope instead to see if an arbitrary
// required setting is present. if it is, then we can assume that the system
// scope should be used.
else if (m_scopes.scopeContains(
settingName(Setting::kScreenName),
ConfigScopes::Scope::System)) {
setLoadFromSystemScope(true);
}
}
void AppConfig::loadSerialKey() {
void AppConfig::recallSerialKey() {
using enum Setting;
// only set the serial key if the current settings scope has they key.
bool shouldLoad = m_Deps.scopes().hasSetting(
settingName(kLoadSystemSettings), ConfigScopes::Scope::Current);
if (!shouldLoad) {
if (!m_scopes.scopeContains(settingName(kLoadSystemSettings))) {
qDebug("no serial key in current scope, skipping");
return;
}
const auto &serialKey =
loadSetting(kSerialKey, m_SerialKey).toString().trimmed();
getFromCurrentScope(kSerialKey, m_SerialKey).toString().trimmed();
if (serialKey.isEmpty()) {
qDebug("serial key is empty, skipping");
@ -273,18 +291,18 @@ void AppConfig::loadSerialKey() {
m_SerialKey = serialKey;
}
void AppConfig::loadElevateMode() {
void AppConfig::recallElevateMode() {
using enum Setting;
if (!m_Deps.scopes().hasSetting(settingName(kElevateMode))) {
if (!m_scopes.scopeContains(settingName(kElevateMode))) {
qDebug("elevate mode not set yet, skipping");
return;
}
QVariant elevateMode = loadSetting(kElevateMode);
QVariant elevateMode = getFromCurrentScope(kElevateMode);
if (!elevateMode.isValid()) {
qDebug("elevate mode not valid, loading legacy setting");
elevateMode = loadSetting(
elevateMode = getFromCurrentScope(
kElevateModeLegacy, QVariant(static_cast<int>(kDefaultElevateMode)));
}
@ -292,7 +310,7 @@ void AppConfig::loadElevateMode() {
}
QString AppConfig::defaultTlsCertPath() const {
QDir path(m_Deps.profileDir());
QDir path(m_pDeps->profileDir());
path = path.filePath("SSL");
path = path.filePath("Synergy.pem");
return path.absolutePath();
@ -303,103 +321,113 @@ QString AppConfig::settingName(Setting name) {
return m_SettingsName[index];
}
template <typename T> void AppConfig::setSetting(Setting name, T value) {
m_Deps.scopes().setSetting(settingName(name), value);
template <typename T> void AppConfig::setInCurrentScope(Setting name, T value) {
m_scopes.setInScope(settingName(name), value);
}
template <typename T> void AppConfig::setCommonSetting(Setting name, T value) {
m_Deps.scopes().setSetting(
settingName(name), value, ConfigScopes::Scope::User);
m_Deps.scopes().setSetting(
settingName(name), value, ConfigScopes::Scope::System);
template <typename T> void AppConfig::saveToAllScopes(Setting name, T value) {
m_scopes.setInScope(settingName(name), value, ConfigScopes::Scope::User);
m_scopes.setInScope(settingName(name), value, ConfigScopes::Scope::System);
}
QVariant AppConfig::loadSetting(Setting name, const QVariant &defaultValue) {
return m_Deps.scopes().loadSetting(settingName(name), defaultValue);
QVariant AppConfig::getFromCurrentScope(
Setting name, const QVariant &defaultValue) const {
return m_scopes.getFromScope(settingName(name), defaultValue);
}
template <typename T>
std::optional<T>
AppConfig::loadOptional(Setting name, std::function<T(QVariant)> toType) const {
if (m_Deps.scopes().hasSetting(settingName(name))) {
return toType(m_Deps.scopes().loadSetting(settingName(name)));
std::optional<T> AppConfig::getFromCurrentScope(
Setting name, std::function<T(const QVariant &)> toType) const {
if (m_scopes.scopeContains(settingName(name))) {
return toType(m_scopes.getFromScope(settingName(name)));
} else {
return std::nullopt;
}
}
template <typename T>
void AppConfig::setOptional(Setting name, const std::optional<T> &value) {
void AppConfig::setInCurrentScope(Setting name, const std::optional<T> &value) {
if (value.has_value()) {
m_Deps.scopes().setSetting(settingName(name), value.value());
m_scopes.setInScope(settingName(name), value.value());
}
}
QVariant
AppConfig::loadCommonSetting(Setting name, const QVariant &defaultValue) const {
AppConfig::findInAllScopes(Setting name, const QVariant &defaultValue) const {
using enum ConfigScopes::Scope;
QVariant result(defaultValue);
QString setting(settingName(name));
if (m_Deps.scopes().hasSetting(setting)) {
result = m_Deps.scopes().loadSetting(setting, defaultValue);
} else if (m_Deps.scopes().getScope() == ConfigScopes::Scope::System) {
if (m_Deps.scopes().hasSetting(setting, ConfigScopes::Scope::User)) {
result = m_Deps.scopes().loadSetting(
setting, defaultValue, ConfigScopes::Scope::User);
if (m_scopes.scopeContains(setting)) {
result = m_scopes.getFromScope(setting, defaultValue);
} else if (m_scopes.activeScope() == System) {
if (m_scopes.scopeContains(setting, User)) {
result = m_scopes.getFromScope(setting, defaultValue, User);
}
} else if (m_Deps.scopes().hasSetting(setting, ConfigScopes::Scope::System)) {
result = m_Deps.scopes().loadSetting(
setting, defaultValue, ConfigScopes::Scope::System);
} else if (m_scopes.scopeContains(setting, System)) {
result = m_scopes.getFromScope(setting, defaultValue, System);
}
return result;
}
void AppConfig::loadScope(ConfigScopes::Scope scope) {
using enum ConfigScopes::Scope;
if (m_Deps.scopes().getScope() != scope) {
setDefaultValues();
m_Deps.scopes().setScope(scope);
if (m_Deps.scopes().hasSetting(
settingName(Setting::kScreenName), m_Deps.scopes().getScope())) {
// If the user already has settings, then load them up now.
m_Deps.scopes().loadAll();
}
switch (scope) {
case User:
qDebug("loading user settings scope");
break;
case System:
qDebug("loading system settings scope");
break;
default:
qFatal("invalid scope");
}
if (m_scopes.activeScope() == scope) {
qDebug("already in required scope, skipping");
return;
}
m_scopes.setActiveScope(scope);
// only signal ready if there is at least one setting in the required scope.
// this prevents the current settings from being set back to default.
if (m_scopes.scopeContains(
settingName(Setting::kScreenName), m_scopes.activeScope())) {
m_scopes.signalReady();
} else {
qDebug("no screen name in scope, skipping");
}
}
void AppConfig::setDefaultValues() { m_InvertConnection = false; }
void AppConfig::setLoadFromSystemScope(bool value) {
using enum ConfigScopes::Scope;
if (value) {
qDebug("loading system settings scope");
loadScope(ConfigScopes::Scope::System);
loadScope(System);
} else {
qDebug("loading user settings scope");
loadScope(ConfigScopes::Scope::User);
loadScope(User);
}
/*
* It's very imprortant to set this variable after loadScope
* because during scope loading this variable can be rewritten with old value
*/
// set after loading scope since it may have been overridden.
m_LoadFromSystemScope = value;
}
bool AppConfig::isWritable() const { return m_Deps.scopes().isWritable(); }
bool AppConfig::isSystemScoped() const {
return m_Deps.scopes().getScope() == ConfigScopes::Scope::System;
bool AppConfig::isActiveScopeWritable() const {
return m_scopes.isActiveScopeWritable();
}
template <typename T>
void AppConfig::setSettingModified(T &variable, const T &newValue) {
if (variable != newValue) {
variable = newValue;
setModified(true);
}
bool AppConfig::isActiveScopeSystem() const {
return m_scopes.activeScope() == ConfigScopes::Scope::System;
}
QString AppConfig::logDir() const {
// by default log to home dir
return QDir::home().absolutePath() + "/";
@ -418,7 +446,7 @@ void AppConfig::persistLogDir() const {
// Begin getters
///////////////////////////////////////////////////////////////////////////////
IConfigScopes &AppConfig::scopes() { return m_Deps.scopes(); }
IConfigScopes &AppConfig::scopes() { return m_scopes; }
bool AppConfig::activationHasRun() const { return m_ActivationHasRun; }
@ -439,7 +467,7 @@ const QString &AppConfig::logFilename() const { return m_LogFilename; }
QString AppConfig::logLevelText() const { return logLevelNames[logLevel()]; }
ProcessMode AppConfig::processMode() const {
return m_ServiceEnabled ? ProcessMode::kService : ProcessMode::kDesktop;
return m_EnableService ? ProcessMode::kService : ProcessMode::kDesktop;
}
bool AppConfig::wizardShouldRun() const {
@ -478,7 +506,7 @@ QString AppConfig::tlsCertPath() const { return m_TlsCertPath; }
QString AppConfig::tlsKeyLength() const { return m_TlsKeyLength; }
bool AppConfig::serviceEnabled() const { return m_ServiceEnabled; }
bool AppConfig::enableService() const { return m_EnableService; }
bool AppConfig::closeToTray() const { return m_CloseToTray; }
@ -504,6 +532,10 @@ std::optional<QPoint> AppConfig::mainWindowPosition() const {
return m_MainWindowPosition;
}
bool AppConfig::showDevThanks() const { return m_ShowDevThanks; }
bool AppConfig::showCloseReminder() const { return m_ShowCloseReminder; }
///////////////////////////////////////////////////////////////////////////////
// End getters
///////////////////////////////////////////////////////////////////////////////
@ -515,109 +547,94 @@ std::optional<QPoint> AppConfig::mainWindowPosition() const {
void AppConfig::clearSerialKey() { m_SerialKey.clear(); }
void AppConfig::setTlsEnabled(bool value) {
setSettingModified(m_TlsEnabled, value);
m_TlsChanged = true;
// we purposefully do not set the 'tls changed' flag when enabling/disabling
// tls, since that would cause the certificate to regenerate, which could get
// pretty annoying.
m_TlsEnabled = value;
}
void AppConfig::setTlsCertPath(const QString &value) {
setSettingModified(m_TlsCertPath, value);
m_TlsChanged = true;
m_TlsChanged = m_TlsCertPath != value;
m_TlsCertPath = value;
}
void AppConfig::setTlsKeyLength(const QString &value) {
setSettingModified(m_TlsKeyLength, value);
m_TlsChanged = true;
m_TlsChanged = m_TlsKeyLength != value;
m_TlsKeyLength = value;
}
void AppConfig::setSerialKey(const QString &serialKey) {
setSettingModified(m_SerialKey, serialKey);
setCommonSetting(Setting::kSerialKey, m_SerialKey);
m_SerialKey = serialKey;
saveToAllScopes(Setting::kSerialKey, m_SerialKey);
}
void AppConfig::setServerGroupChecked(bool newValue) {
setSettingModified(m_ServerGroupChecked, newValue);
m_ServerGroupChecked = newValue;
}
void AppConfig::setUseExternalConfig(bool newValue) {
setSettingModified(m_UseExternalConfig, newValue);
m_UseExternalConfig = newValue;
}
void AppConfig::setConfigFile(const QString &newValue) {
setSettingModified(m_ConfigFile, newValue);
m_ConfigFile = newValue;
}
void AppConfig::setUseInternalConfig(bool newValue) {
setSettingModified(m_UseInternalConfig, newValue);
m_UseInternalConfig = newValue;
}
void AppConfig::setClientGroupChecked(bool newValue) {
setSettingModified(m_ClientGroupChecked, newValue);
m_ClientGroupChecked = newValue;
}
void AppConfig::setServerHostname(const QString &newValue) {
setSettingModified(m_ServerHostname, newValue);
m_ServerHostname = newValue;
}
void AppConfig::setLastVersion(const QString &version) {
setSettingModified(m_LastVersion, version);
m_LastVersion = version;
}
void AppConfig::setScreenName(const QString &s) {
setSettingModified(m_ScreenName, s);
m_ScreenName = s;
emit screenNameChanged();
}
void AppConfig::setPort(int i) { setSettingModified(m_Port, i); }
void AppConfig::setPort(int i) { m_Port = i; }
void AppConfig::setNetworkInterface(const QString &s) {
setSettingModified(m_Interface, s);
}
void AppConfig::setNetworkInterface(const QString &s) { m_Interface = s; }
void AppConfig::setLogLevel(int i) { setSettingModified(m_LogLevel, i); }
void AppConfig::setLogLevel(int i) { m_LogLevel = i; }
void AppConfig::setLogToFile(bool b) { setSettingModified(m_LogToFile, b); }
void AppConfig::setLogToFile(bool b) { m_LogToFile = b; }
void AppConfig::setLogFilename(const QString &s) {
setSettingModified(m_LogFilename, s);
}
void AppConfig::setLogFilename(const QString &s) { m_LogFilename = s; }
void AppConfig::setWizardHasRun() {
setSettingModified(m_WizardLastRun, kWizardVersion);
}
void AppConfig::setWizardHasRun() { m_WizardLastRun = kWizardVersion; }
void AppConfig::setStartedBefore(bool b) {
setSettingModified(m_StartedBefore, b);
}
void AppConfig::setStartedBefore(bool b) { m_StartedBefore = b; }
void AppConfig::setElevateMode(ElevateMode em) {
setSettingModified(m_ElevateMode, em);
}
void AppConfig::setElevateMode(ElevateMode em) { m_ElevateMode = em; }
void AppConfig::setAutoHide(bool b) { setSettingModified(m_AutoHide, b); }
void AppConfig::setAutoHide(bool b) { m_AutoHide = b; }
void AppConfig::setLicenseNextCheck(unsigned long long time) {
setSettingModified(m_licenseNextCheck, time);
m_licenseNextCheck = time;
}
void AppConfig::setInvertScrollDirection(bool newValue) {
setSettingModified(m_InvertScrollDirection, newValue);
m_InvertScrollDirection = newValue;
}
void AppConfig::setLanguageSync(bool newValue) {
setSettingModified(m_LanguageSync, newValue);
}
void AppConfig::setLanguageSync(bool newValue) { m_LanguageSync = newValue; }
void AppConfig::setPreventSleep(bool newValue) {
setSettingModified(m_PreventSleep, newValue);
}
void AppConfig::setPreventSleep(bool newValue) { m_PreventSleep = newValue; }
void AppConfig::setServiceEnabled(bool enabled) {
setSettingModified(m_ServiceEnabled, enabled);
}
void AppConfig::setEnableService(bool enabled) { m_EnableService = enabled; }
void AppConfig::setCloseToTray(bool minimize) {
setSettingModified(m_CloseToTray, minimize);
}
void AppConfig::setCloseToTray(bool minimize) { m_CloseToTray = minimize; }
void AppConfig::setInvertConnection(bool value) {
setSettingModified(m_InvertConnection, value);
m_InvertConnection = value;
emit invertConnectionChanged();
}
@ -629,6 +646,12 @@ void AppConfig::setMainWindowPosition(const QPoint &position) {
m_MainWindowPosition = position;
}
void AppConfig::setShowDevThanks(bool value) { m_ShowDevThanks = value; }
void AppConfig::setShowCloseReminder(bool value) {
m_ShowCloseReminder = value;
}
///////////////////////////////////////////////////////////////////////////////
// End setters
///////////////////////////////////////////////////////////////////////////////

View File

@ -18,7 +18,6 @@
#pragma once
#include "CommonConfig.h"
#include "ConfigScopes.h"
#include "CoreInterface.h"
#include "ElevateMode.h"
@ -31,7 +30,7 @@
#include <QSize>
#include <QString>
#include <QVariant>
#include <mutex>
#include <optional>
enum class ProcessMode { kService, kDesktop };
@ -42,7 +41,13 @@ const QString kDefaultLogFile = "synergy.log";
const ProcessMode kDefaultProcessMode = ProcessMode::kService;
#else
const ProcessMode kDefaultProcessMode = ProcessMode::kDesktop;
#endif
#endif // Q_OS_WIN
#ifdef SYNERGY_SHOW_DEV_THANKS
const bool kDefaultShowDevThanks = true;
#else
const bool kDefaultShowDevThanks = false;
#endif // SYNERGY_SHOW_DEV_THANKS
/**
* @brief Simply reads and writes app settings.
@ -52,9 +57,7 @@ const ProcessMode kDefaultProcessMode = ProcessMode::kDesktop;
* instance is widely accessible, but that has previously led to this class
* becoming a god object.
*/
class AppConfig : public QObject,
public synergy::gui::CommonConfig,
public synergy::gui::IAppConfig {
class AppConfig : public QObject, public synergy::gui::IAppConfig {
Q_OBJECT
private:
@ -96,10 +99,12 @@ private:
kInvertConnection = 34,
// 35 = client-host-mode, obsolete
// 36 = server-client-mode, obsolete
kServiceEnabled = 37,
kEnableService = 37,
kCloseToTray = 38,
kMainWindowSize = 39,
kMainWindowPosition = 40,
kShowDevThanks = 41,
kShowCloseReminder = 42,
};
public:
@ -108,29 +113,33 @@ public:
virtual QString profileDir() const {
return m_coreInterface.getProfileDir();
}
virtual synergy::gui::IConfigScopes &scopes() { return m_Scopes; }
virtual QString hostname() const { return QHostInfo::localHostName(); }
private:
[[no_unique_address]] CoreInterface m_coreInterface;
synergy::gui::ConfigScopes m_Scopes;
};
explicit AppConfig() : AppConfig(s_Deps) {}
explicit AppConfig(Deps &deps);
explicit AppConfig(
synergy::gui::IConfigScopes &scopes,
std::shared_ptr<Deps> deps = std::make_shared<Deps>());
synergy::gui::IConfigScopes &scopes();
void saveSettings() override;
void loadAllScopes();
void loadSettings() override;
/**
* @brief Commits the current settings to the active scope.
* This should only be called when the settings are about to be saved.
*/
void commit();
void determineScope();
/**
* Getters
*/
void setActivationHasRun(bool value);
bool isWritable() const;
bool isSystemScoped() const;
bool isActiveScopeWritable() const;
bool isActiveScopeSystem() const;
const QString &screenName() const;
int port() const;
const QString &networkInterface() const;
@ -159,7 +168,7 @@ public:
bool clientGroupChecked() const;
QString serverHostname() const;
QString lastVersion() const;
bool serviceEnabled() const;
bool enableService() const;
bool closeToTray() const;
QString serialKey() const;
bool activationHasRun() const;
@ -168,6 +177,8 @@ public:
QString tlsKeyLength() const override;
std::optional<QSize> mainWindowSize() const;
std::optional<QPoint> mainWindowPosition() const;
bool showDevThanks() const;
bool showCloseReminder() const;
/**
* Setters
@ -197,13 +208,15 @@ public:
void setClientGroupChecked(bool);
void setServerHostname(const QString &);
void setLastVersion(const QString &version);
void setServiceEnabled(bool enabled);
void setEnableService(bool enabled);
void setCloseToTray(bool minimize);
void setTlsCertPath(const QString &path);
void setTlsKeyLength(const QString &length);
void setInvertConnection(bool value);
void setMainWindowSize(const QSize &size);
void setMainWindowPosition(const QPoint &position);
void setShowDevThanks(bool show);
void setShowCloseReminder(bool show);
/// @brief Sets the user preference to load from SystemScope.
/// @param [in] value
@ -214,11 +227,13 @@ public:
private:
static QString settingName(AppConfig::Setting name);
void loadScreenName();
void loadSerialKey();
void loadElevateMode();
void loadCommonSettings();
void loadScopeSettings();
void recall();
void recallScreenName();
void recallSerialKey();
void recallElevateMode();
void recallFromAllScopes();
void recallFromCurrentScope();
/**
* @brief Loads a setting if it exists, otherwise returns `std::nullopt`
@ -226,60 +241,48 @@ private:
* @param toType A function to convert the QVariant to the desired type.
*/
template <typename T>
std::optional<T>
loadOptional(Setting name, std::function<T(QVariant)> toType) const;
std::optional<T> getFromCurrentScope(
Setting name, std::function<T(const QVariant &)> toType) const;
/**
* @brief Sets a setting if the value is not `std::nullopt`.
*/
template <typename T>
void setOptional(Setting name, const std::optional<T> &value);
void setInCurrentScope(Setting name, const std::optional<T> &value);
/// @brief Sets the value of a setting
/// @param [in] name The Setting to be saved
/// @param [in] value The Value to be saved
template <typename T> void setSetting(AppConfig::Setting name, T value);
template <typename T>
void setInCurrentScope(AppConfig::Setting name, T value);
/// @brief Sets the value of a common setting
/// which should have the same value for all scopes
/// @param [in] name The Setting to be saved
/// @param [in] value The Value to be saved
template <typename T> void setCommonSetting(AppConfig::Setting name, T value);
template <typename T> void saveToAllScopes(AppConfig::Setting name, T value);
/// @brief Loads a setting
/// @param [in] name The setting to be loaded
/// @param [in] defaultValue The default value of the setting
QVariant loadSetting(
AppConfig::Setting name, const QVariant &defaultValue = QVariant());
/// @brief Loads a common setting
/// @param [in] name The setting to be loaded
/// @param [in] defaultValue The default value of the setting
QVariant loadCommonSetting(
QVariant getFromCurrentScope(
AppConfig::Setting name, const QVariant &defaultValue = QVariant()) const;
/// @brief Sets the setting in the config checking if it has changed and
/// flagging that settings
/// needs to be saved if the setting was different
/// @param [in] variable the setting that will be changed
/// @param [in] newValue The new value of the setting
template <typename T> void setSettingModified(T &variable, const T &newValue);
/**
* @brief Finds a value by searching each scope starting with the current
* scope.
*/
QVariant findInAllScopes(
AppConfig::Setting name, const QVariant &defaultValue = QVariant()) const;
/// @brief This method loads config from specified scope
/// @param [in] scope which should be loaded.
void loadScope(synergy::gui::ConfigScopes::Scope scope);
/// @brief This function sets default values
/// for settings that shouldn't be copied from between scopes.
void setDefaultValues();
/**
* @brief Gets a TLS certificate path based on the user's profile dir.
*/
QString defaultTlsCertPath() const;
static Deps s_Deps;
Deps &m_Deps;
synergy::gui::IConfigScopes &m_scopes;
std::shared_ptr<Deps> m_pDeps;
QString m_ScreenName;
int m_Port = 24800;
QString m_Interface = "";
@ -306,12 +309,15 @@ private:
bool m_UseInternalConfig = false;
bool m_ClientGroupChecked = false;
QString m_ServerHostname = "";
bool m_ServiceEnabled = kDefaultProcessMode == ProcessMode::kService;
bool m_CloseToTray = false;
bool m_EnableService = kDefaultProcessMode == ProcessMode::kService;
bool m_CloseToTray = true;
QString m_TlsCertPath = defaultTlsCertPath();
QString m_TlsKeyLength = "2048";
std::optional<QSize> m_MainWindowSize;
std::optional<QPoint> m_MainWindowPosition;
bool m_ShowDevThanks = kDefaultShowDevThanks;
bool m_LoadFromSystemScope = false;
bool m_ShowCloseReminder = true;
/**
* @brief Flag is set when any TLS is setting is changed, and is reset
@ -319,19 +325,6 @@ private:
*/
bool m_TlsChanged = false;
/// @brief should the setting be loaded from
/// SystemScope
/// If the user has settings but this is
/// true then system settings will be
/// loaded instead of the users
bool m_LoadFromSystemScope = false;
/// @brief As the settings will be accessible by multiple objects this lock
/// will ensure that
/// it cant be modified by more that one object at a time if the
/// setting is being switched from system to user.
std::mutex m_settings_lock;
static const char m_CoreServerName[];
static const char m_CoreClientName[];
static const char m_LogDir[];
@ -339,12 +332,10 @@ private:
/// @brief Contains the string values of the settings names that will be saved
static const char *const m_SettingsName[];
/// @brief Contains the name of the default configuration filename
/// @brief Core config filename (not the Qt settings filename)
static const char m_ConfigFilename[];
signals:
void loaded();
void saved();
void tlsChanged();
void screenNameChanged();
void invertConnectionChanged();

View File

@ -17,58 +17,81 @@
#include "ConfigScopes.h"
#include "CommonConfig.h"
#include <QCoreApplication>
#include <QFile>
#include <cassert>
#include <memory>
const auto kSystemConfigFilename = "SystemConfig.ini";
const auto kUnixSystemConfigPath = "/usr/local/etc/symless/";
namespace synergy::gui {
QString getSystemSettingPath() {
const QString settingFilename("SystemConfig.ini");
QString path;
const QString settingFilename(kSystemConfigFilename);
#if defined(Q_OS_WIN)
path = QCoreApplication::applicationDirPath() + "\\";
return QCoreApplication::applicationDirPath() + "\\";
#elif defined(Q_OS_DARWIN)
// Global preferances dir
// Would be nice to use /library, but QT has no elevate system in place
path = "/usr/local/etc/symless/";
// it would be nice to use /Library dir, but qt has no elevate system.
return kUnixSystemConfigPath + settingFilename;
#elif defined(Q_OS_LINUX)
// QT adds application and filename to the end of the path already on linux
path = "/usr/local/etc/symless/";
return path;
// qt already adds application and filename to the end of the path on linux.
return kUnixSystemConfigPath;
#else
assert("OS not supported");
qFatal("unsupported platform");
return "";
#endif
return path + settingFilename;
}
#if defined(Q_OS_WIN)
void loadWindowsLegacy(QSettings &settings) {
if (!QFile(settings.fileName()).exists()) {
QSettings::setPath(
QSettings::IniFormat, QSettings::SystemScope, "SystemConfig.ini");
QSettings oldSystemSettings(
QSettings::IniFormat, QSettings::SystemScope,
QCoreApplication::organizationName(),
QCoreApplication::applicationName());
if (QFile(oldSystemSettings.fileName()).exists()) {
for (const auto &key : oldSystemSettings.allKeys()) {
settings.setValue(key, oldSystemSettings.value(key));
}
}
// Restore system settings path
QSettings::setPath(
QSettings::IniFormat, QSettings::SystemScope, getSystemSettingPath());
if (QFile(settings.fileName()).exists()) {
qDebug("system settings already exist, skipping legacy load");
return;
}
QSettings::setPath(
QSettings::IniFormat, QSettings::SystemScope, kSystemConfigFilename);
QSettings oldSystemSettings(
QSettings::IniFormat, QSettings::SystemScope,
QCoreApplication::organizationName(),
QCoreApplication::applicationName());
if (QFile(oldSystemSettings.fileName()).exists()) {
for (const auto &key : oldSystemSettings.allKeys()) {
settings.setValue(key, oldSystemSettings.value(key));
}
}
QSettings::setPath(
QSettings::IniFormat, QSettings::SystemScope, getSystemSettingPath());
}
#endif
ConfigScopes::ConfigScopes() {
auto orgName = QCoreApplication::organizationName();
if (orgName.isEmpty()) {
qFatal("unable to load config, organization name is empty");
return;
} else {
qDebug() << "org name for config:" << orgName;
}
auto appName = QCoreApplication::applicationName();
if (appName.isEmpty()) {
qFatal("unable to load config, application name is empty");
return;
} else {
qDebug() << "app name for config:" << appName;
}
// default to user scope.
// if we set the scope specifically then we also have to set the application
// name and the organisation name which breaks backwards compatibility.
m_pUserSettings = std::make_unique<QSettings>();
qDebug() << "user settings path:" << m_pUserSettings->fileName();
QSettings::setPath(
QSettings::Format::IniFormat, QSettings::Scope::SystemScope,
getSystemSettingPath());
@ -76,47 +99,57 @@ ConfigScopes::ConfigScopes() {
// Config will default to User settings if they exist,
// otherwise it will load System setting and save them to User settings
m_pSystemSettings = std::make_unique<QSettings>(
QSettings::Format::IniFormat, QSettings::Scope::SystemScope,
QCoreApplication::organizationName(),
QCoreApplication::applicationName());
QSettings::Format::IniFormat, QSettings::Scope::SystemScope, orgName,
appName);
// default to user scope.
// if we set the scope specifically then we also have to set the application
// name and the organisation name which breaks backwards compatibility.
m_pUserSettings = std::make_unique<QSettings>();
qDebug() << "system settings path:" << m_pSystemSettings->fileName();
load();
}
ConfigScopes::~ConfigScopes() {
while (!m_pReceivers.empty()) {
m_pReceivers.pop_back();
}
}
void ConfigScopes::load() {
#if defined(Q_OS_WIN)
// This call is needed for backwardcapability with old settings.
loadWindowsLegacy(*m_pSystemSettings);
#endif
}
bool ConfigScopes::hasSetting(const QString &name, Scope scope) const {
void ConfigScopes::signalReady() { emit ready(); }
void ConfigScopes::save() {
qDebug("emitting config saving signal");
emit saving();
qDebug("writing config to filesystem");
m_pUserSettings->sync();
m_pSystemSettings->sync();
}
bool ConfigScopes::isActiveScopeWritable() const {
return activeSettings()->isWritable();
}
void ConfigScopes::setActiveScope(ConfigScopes::Scope scope) {
m_currentScope = scope;
}
ConfigScopes::Scope ConfigScopes::activeScope() const { return m_currentScope; }
bool ConfigScopes::scopeContains(const QString &name, Scope scope) const {
switch (scope) {
case Scope::User:
return m_pUserSettings->contains(name);
case Scope::System:
return m_pSystemSettings->contains(name);
default:
return currentSettings()->contains(name);
return activeSettings()->contains(name);
}
}
bool ConfigScopes::isWritable() const {
return currentSettings()->isWritable();
QSettings *ConfigScopes::activeSettings() const {
if (m_currentScope == Scope::User) {
return m_pUserSettings.get();
} else {
return m_pSystemSettings.get();
}
}
QVariant ConfigScopes::loadSetting(
QVariant ConfigScopes::getFromScope(
const QString &name, const QVariant &defaultValue, Scope scope) const {
switch (scope) {
case Scope::User:
@ -124,68 +157,23 @@ QVariant ConfigScopes::loadSetting(
case Scope::System:
return m_pSystemSettings->value(name, defaultValue);
default:
return currentSettings()->value(name, defaultValue);
return activeSettings()->value(name, defaultValue);
}
}
void ConfigScopes::setScope(ConfigScopes::Scope scope) {
m_CurrentScope = scope;
}
ConfigScopes::Scope ConfigScopes::getScope() const { return m_CurrentScope; }
void ConfigScopes::loadAll() {
for (auto &i : m_pReceivers) {
i->loadSettings();
}
}
void ConfigScopes::saveAll() {
// Save if there are any unsaved changes otherwise skip
if (unsavedChanges()) {
for (auto &i : m_pReceivers) {
i->saveSettings();
}
m_pUserSettings->sync();
m_pSystemSettings->sync();
m_unsavedChanges = false;
}
}
QSettings *ConfigScopes::currentSettings() const {
if (m_CurrentScope == Scope::User) {
return m_pUserSettings.get();
} else {
return m_pSystemSettings.get();
}
}
void ConfigScopes::registerReceiver(CommonConfig *receiver) {
m_pReceivers.push_back(receiver);
}
bool ConfigScopes::unsavedChanges() const {
if (m_unsavedChanges) {
return true;
}
for (const auto &i : m_pReceivers) {
if (i->modified()) {
return true;
}
}
return false;
}
void ConfigScopes::markUnsaved() { m_unsavedChanges = true; }
void ConfigScopes::setSetting(
void ConfigScopes::setInScope(
const QString &name, const QVariant &value, Scope scope) {
currentSettings()->setValue(name, value);
m_unsavedChanges = true;
switch (scope) {
case Scope::User:
m_pUserSettings->setValue(name, value);
break;
case Scope::System:
m_pSystemSettings->setValue(name, value);
break;
default:
activeSettings()->setValue(name, value);
break;
}
}
} // namespace synergy::gui

View File

@ -19,94 +19,44 @@
#include "IConfigScopes.h"
#include <QObject>
#include <QSettings>
#include <QVariant>
#include <memory>
namespace synergy::gui {
class CommonConfig;
/// @brief A general config reader and writer for user and gloabl settings
class ConfigScopes : public IConfigScopes {
/// @brief Encapsulates Qt config for both user and global scopes.
class ConfigScopes : public QObject, public IConfigScopes {
Q_OBJECT
public:
explicit ConfigScopes();
virtual ~ConfigScopes();
virtual ~ConfigScopes() = default;
/// @brief Checks if the setting exists
/// @param [in] name The name of the setting to check
/// @param [in] scope The scope to search in
/// @return bool True if the current scope has the named setting
bool
hasSetting(const QString &name, Scope scope = Scope::Current) const override;
/// @brief Checks if the current scope settings writable
/// @return bool True if the current scope writable
bool isWritable() const override;
/// @brief Sets the value of a setting
/// @param [in] name The Setting to be saved
/// @param [in] value The Value to be saved (Templated)
/// @param [in] scope The scope to get the value from, default is current
/// scope
void setSetting(
void signalReady() override;
void save() override;
bool scopeContains(
const QString &name, Scope scope = Scope::Current) const override;
bool isActiveScopeWritable() const override;
void setInScope(
const QString &name, const QVariant &value,
Scope scope = Scope::Current) override;
/// @brief Loads a setting
/// @param [in] name The setting to be loaded
/// @param [in] defaultValue The default value of the setting
/// @param [in] scope The scope to get the value from, default is current
/// scope
QVariant loadSetting(
QVariant getFromScope(
const QString &name, const QVariant &defaultValue = QVariant(),
Scope scope = Scope::Current) const override;
void setActiveScope(Scope scope = Scope::User) override;
Scope activeScope() const override;
QSettings *activeSettings() const override;
/// @brief Changes the setting save and load location between System and User
/// scope
/// @param [in] scope The scope to set
void setScope(Scope scope = Scope::User) override;
/// @brief Get the current scope the settings are loading and save from.
/// @return Scope An enum defining the current scope
Scope getScope() const override;
/// @brief trigger a config load across all registered classes
void loadAll() override;
/// @brief trigger a config save across all registered classes
void saveAll() override;
/// @brief Returns the current scopes settings object
/// If more specialize control into the settings is needed this can
/// provide direct access to the settings file handler
/// @return QSettings The Settings object as a reference
QSettings *currentSettings() const override;
/// @brief This marks the settings as unsaved if the settings() was used to
/// directly affect the config file
void markUnsaved();
/// @brief Register a class to receives requests to save and load settings
void registerReceiver(CommonConfig *receiver) override;
/// @brief Checks if any registered class has any unsaved changes
/// @return bool True if any registered class has unsaved changes
bool unsavedChanges() const;
signals:
void ready();
void saving();
private:
void load();
Scope m_CurrentScope = Scope::User;
Scope m_currentScope = Scope::User;
std::unique_ptr<QSettings> m_pUserSettings;
std::unique_ptr<QSettings> m_pSystemSettings;
/// @brief Receivers of load/save callbacks
std::list<CommonConfig *> m_pReceivers;
/// @brief Is set to true when settings are changed
bool m_unsavedChanges = false;
};
} // namespace synergy::gui

View File

@ -17,8 +17,6 @@
#pragma once
#include "gui/CommonConfig.h"
#include <QSettings>
#include <QString>
#include <QVariant>
@ -31,21 +29,42 @@ public:
virtual ~IConfigScopes() = default;
virtual void registerReceiver(CommonConfig *receiver) = 0;
virtual void loadAll() = 0;
virtual Scope activeScope() const = 0;
virtual void setActiveScope(Scope scope = Scope::User) = 0;
virtual bool isActiveScopeWritable() const = 0;
virtual QSettings *activeSettings() const = 0;
/**
* @brief Signals to listeners that the settings that they should read.
*/
virtual void signalReady() = 0;
/**
* @brief Signalls to listeners to save and calls `sync` on underlying Qt
* config.
*
*/
virtual void save() = 0;
/**
* @brief Check a scope for a config value (default is current scope).
*/
virtual bool
hasSetting(const QString &name, Scope scope = Scope::Current) const = 0;
virtual QVariant loadSetting(
scopeContains(const QString &name, Scope scope = Scope::Current) const = 0;
/**
* @brief Load a config value from a scope (default is current scope).
*/
virtual QVariant getFromScope(
const QString &name, const QVariant &defaultValue = QVariant(),
Scope scope = Scope::Current) const = 0;
virtual void setSetting(
/**
* @brief Set a config value in a scope (default is current scope).
*/
virtual void setInScope(
const QString &name, const QVariant &value,
Scope scope = Scope::Current) = 0;
virtual Scope getScope() const = 0;
virtual void setScope(Scope scope = Scope::User) = 0;
virtual bool isWritable() const = 0;
virtual QSettings *currentSettings() const = 0;
virtual void saveAll() = 0;
};
} // namespace synergy::gui

View File

@ -17,6 +17,7 @@
#include "LicenseHandler.h"
#include "constants.h"
#include "license/ProductEdition.h"
#include "license/parse_serial_key.h"
@ -41,6 +42,10 @@ LicenseHandler::ChangeSerialKeyResult
LicenseHandler::changeSerialKey(const QString &hexString) {
using enum LicenseHandler::ChangeSerialKeyResult;
if (!m_enabled) {
qFatal("cannot set serial key, licensing is disabled");
}
if (hexString.isEmpty()) {
qFatal("serial key is empty");
return kFatal;

View File

@ -17,6 +17,7 @@
#pragma once
#include "gui/constants.h"
#include "license/License.h"
#include "license/ProductEdition.h"
@ -44,11 +45,13 @@ public:
void validate() const;
QString productName() const;
ChangeSerialKeyResult changeSerialKey(const QString &hexString);
void setEnabled(bool enabled) { m_enabled = enabled; }
signals:
void serialKeyChanged(const QString &serialKey) const;
void invalidLicense() const;
private:
bool m_enabled = kLicensingEnabled;
License m_license = License::invalid();
};

View File

@ -56,13 +56,13 @@ QIpcClient::~QIpcClient() {
void QIpcClient::connected() {
sendHello();
infoMessage("connection established");
emit infoMessage("connection established");
}
void QIpcClient::connectToHost() {
m_Enabled = true;
infoMessage("connecting to service...");
emit infoMessage("connecting to service...");
const auto port = static_cast<quint16>(kIpcPort);
m_Socket->connectToHost(QHostAddress(QHostAddress::LocalHost), port);
@ -73,7 +73,7 @@ void QIpcClient::connectToHost() {
}
void QIpcClient::disconnectFromHost() {
infoMessage("service disconnect");
emit infoMessage("service disconnect");
m_Reader->stop();
m_Socket->close();
}
@ -92,7 +92,7 @@ void QIpcClient::error(QAbstractSocket::SocketError error) {
break;
}
errorMessage(QString("ipc connection error, %1").arg(text));
emit errorMessage(QString("ipc connection error, %1").arg(text));
QTimer::singleShot(1000, this, SLOT(retryConnect()));
}
@ -133,7 +133,9 @@ void QIpcClient::sendCommand(
stream->writeRawData(elevateBuf, 1);
}
void QIpcClient::handleReadLogLine(const QString &text) { readLogLine(text); }
void QIpcClient::handleReadLogLine(const QString &text) {
emit readLogLine(text);
}
// TODO: qt must have a built in way of converting int to bytes.
void QIpcClient::intToBytes(int value, char *buffer, int size) {

View File

@ -32,6 +32,9 @@ bool TlsUtility::isAvailableAndEnabled() const {
}
void TlsUtility::generateCertificate(bool replace) const {
qDebug("generating tls certificate, "
"all clients must trust the new fingerprint");
if (!isAvailableAndEnabled()) {
qFatal("unable to generate tls certificate, "
"tls is either not available or not enabled");

View File

@ -19,25 +19,33 @@
#include <QString>
const auto kAppName = "Synergy";
// TODO: change to `com.symless`. we'll need to gracefully import old settings,
// since qt uses this on some platforms when saving settings.
const auto kAppDomain = "https://symless.com";
#ifdef SYNERGY_PRODUCT_NAME
const QString kProductName = SYNERGY_PRODUCT_NAME;
#else
const QString kProductName;
#endif
#ifdef SYNERGY_ENABLE_LICENSING
const bool kLicensingEnabled = true;
#else
const bool kLicensingEnabled = false;
#endif // SYNERGY_ENABLE_LICENSING
const auto kColorWhite = "#ffffff";
const auto kColorPrimary = "#ff7c00";
const auto kColorSecondary = "#4285f4";
const auto kColorTertiary = "#33b2cc";
const auto kColorError = "#ec4c47";
const auto kColorNotice = "#3b67d3";
const auto kLinkBuy = R"(<a href="%1" style="color: %2">Buy now</a>)";
const auto kLinkRenew = R"(<a href="%1" style="color: %2">Renew now</a>)";
const auto kLinkDownload = R"(<a href="%1" style="color: %2">Download now</a>)";
const auto kUrlSourceQuery = "source=gui";
const auto kUrlWebsite = "https://symless.com";
const auto kUrlContribute = "https://github.com/symless/synergy-core";
const auto kUrlGnomeTrayFix =
"https://extensions.gnome.org/extension/2890/tray-icons-reloaded/";
const auto kUrlProduct = QString("%1/synergy").arg(kUrlWebsite);
const auto kUrlPurchase =
QString("%1/purchase?%2").arg(kUrlProduct, kUrlSourceQuery);
@ -46,21 +54,3 @@ const auto kUrlContact =
const auto kUrlHelp = QString("%1/help?%2").arg(kUrlProduct, kUrlSourceQuery);
const auto kUrlDownload =
QString("%1/download?%2").arg(kUrlProduct, kUrlSourceQuery);
const auto kStyleLineEditErrorBorder =
QString("border: 1px solid %1; border-radius: 2px; padding: 2px;")
.arg(kColorError);
const auto kStyleErrorActiveLabel = //
QString("padding: 3px 5px; border-radius: 3px; "
"background-color: %1; color: %2")
.arg(kColorError, kColorWhite);
const auto kStyleErrorInactiveLabel = //
QString("padding: 3px 5px; border-radius: 3px;"
"background-color: none");
const auto kStyleNoticeLabel = //
QString("padding: 3px 5px; border-radius: 3px;"
"background-color: %1; color: %2")
.arg(kColorNotice, kColorWhite);

View File

@ -19,6 +19,7 @@
#include "constants.h"
#include "license/License.h"
#include "styles.h"
using License = synergy::license::License;

159
src/lib/gui/messages.cpp Normal file
View File

@ -0,0 +1,159 @@
/*
* 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 "messages.h"
#include "constants.h"
#include "styles.h"
#include <QDateTime>
#include <QMessageBox>
#include <QTime>
namespace synergy::gui::messages {
void messageHandler(
QtMsgType type, const QMessageLogContext &context, const QString &message) {
auto datetime = QDateTime::currentDateTime().toString("yyyy-MM-ddTHH:mm:ss");
const auto filename = QString(context.file).split("/").last();
auto function = context.function ? context.function : "";
QString typeString;
auto out = stdout;
switch (type) {
case QtDebugMsg:
typeString = "DEBUG";
break;
case QtInfoMsg:
typeString = "INFO";
break;
case QtWarningMsg:
typeString = "WARNING";
out = stderr;
break;
case QtCriticalMsg:
typeString = "CRITICAL";
out = stderr;
break;
case QtFatalMsg:
typeString = "FATAL";
out = stderr;
break;
}
auto logLine = QString("[%1] %2: %3\n\t%4:%5, %6")
.arg(datetime)
.arg(typeString)
.arg(message)
.arg(filename)
.arg(context.line)
.arg(function);
auto logLineUtf = logLine.toUtf8();
auto logLine_c = logLineUtf.constData();
fprintf(out, "%s\n", logLine_c);
if (type == QtFatalMsg) {
auto contextString =
QString("%1:%2\n%3").arg(filename).arg(context.line).arg(function);
QMessageBox::critical(
nullptr, "Fatal error",
QString("<p>Sorry, a fatal error has occurred "
"and the application must now exit.</p>"
"<p>Please "
R"(<a href="%1" style="color: %2">contact us</a>)"
" and copy/paste the following error:</p>"
"<pre>%3\n\n%4</pre>")
.arg(kUrlContact, kColorSecondary, message, contextString));
// developers: if you hit this line in your debugger, traverse the stack to
// find the cause of the fatal error.
// important: crash the app on fatal error to prevent the app being used in
// a broken state.
abort();
}
}
void showCloseReminder(QWidget *parent) {
QString message =
"<p>Synergy will continue to run in the background and can be accessed "
"via the Synergy icon in your system notifications area. This setting "
"can be disabled.</p>";
#if defined(Q_OS_LINUX)
message += QString("<p>On some Linux systems such as GNOME 3, the "
"notification area might be disabled. "
"You may need to "
R"(<a href="%1" %2>enable an extension</a>)"
" to see the Synergy tray icon.</p>")
.arg(kUrlGnomeTrayFix, kStyleLink);
#endif
QMessageBox::information(parent, "Notification area icon", message);
}
void showFirstRunMessage(
QWidget *parent, bool closeToTray, bool enableService, bool isServer) {
auto message = QString("<p>Synergy is now connected!</p>");
if (isServer) {
message +=
"<p>Try moving your mouse to your other computer. Once there, go ahead "
"and type something.</p>"
"<p>Don't forget, you can copy and paste between computers too.</p>";
} else {
message += "<p>Try controlling this computer remotely.</p>";
}
if (!closeToTray && !enableService) {
message +=
"<p>As you do not have the setting enabled to keep Synergy running in "
"the background, you'll need to keep this window open or minimized to "
"keep Synergy running.</p>";
} else {
message +=
"<p>You can now close this window and Synergy will continue to run in "
"the background. This setting can be disabled.</p>";
}
QMessageBox::information(parent, "Connected", message);
}
void showDevThanks(QWidget *parent, const QString &productName) {
if (productName.isEmpty()) {
qFatal("product name not set");
}
QMessageBox::information(
parent, "Thank you!",
QString(
"<p>Thanks for using %1.</p>"
"<p>If you enjoy using this app, you can support the developers by "
R"(<a href="%1" style="color: %2")>purchasing a license</a>)"
" or "
R"(<a href="%3" style="color: %4")>contributing code</a>.)"
"</p>"
"<p>This message will only appear once.</p>")
.arg(
productName, kUrlPurchase, kColorSecondary, kUrlContribute,
kColorSecondary));
}
} // namespace synergy::gui::messages

36
src/lib/gui/messages.h Normal file
View File

@ -0,0 +1,36 @@
/*
* 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 <QMessageLogContext>
#include <QString>
#include <QWidget>
namespace synergy::gui::messages {
void messageHandler(
QtMsgType type, const QMessageLogContext &context, const QString &msg);
void showFirstRunMessage(
QWidget *parent, bool closeToTray, bool enableService, bool isServer);
void showCloseReminder(QWidget *parent);
void showDevThanks(QWidget *parent, const QString &productName);
} // namespace synergy::gui::messages

49
src/lib/gui/styles.h Normal file
View File

@ -0,0 +1,49 @@
/*
* synergy -- mouse and keyboard sharing utility
* Copyright (C) 2014 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 <QString>
const auto kColorWhite = "#ffffff";
const auto kColorPrimary = "#ff7c00";
const auto kColorSecondary = "#4285f4";
const auto kColorTertiary = "#33b2cc";
const auto kColorError = "#ec4c47";
const auto kColorNotice = "#3b67d3";
const auto kColorLightGrey = "#666666";
const auto kStyleLink = //
QString("color: %1").arg(kColorSecondary);
const auto kStyleLineEditErrorBorder =
QString("border: 1px solid %1; border-radius: 2px; padding: 2px;")
.arg(kColorError);
const auto kStyleErrorActiveLabel = //
QString("padding: 3px 5px; border-radius: 3px; "
"background-color: %1; color: %2")
.arg(kColorError, kColorWhite);
const auto kStyleErrorInactiveLabel = //
QString("padding: 3px 5px; border-radius: 3px;"
"background-color: none");
const auto kStyleNoticeLabel = //
QString("padding: 3px 5px; border-radius: 3px;"
"background-color: %1; color: %2")
.arg(kColorNotice, kColorWhite);

38
src/lib/gui/version.cpp Normal file
View File

@ -0,0 +1,38 @@
/*
* 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 "version.h"
const QString kVersion = SYNERGY_VERSION;
#ifdef GIT_SHA_SHORT
const QString kVersionGitSha = GIT_SHA_SHORT;
#else
const QString kVersionGitSha;
#endif
namespace synergy::gui {
QString version() {
QString result(kVersion);
if (!kVersionGitSha.isEmpty()) {
result.append(QString(" (%1)").arg(kVersionGitSha));
}
return result;
}
} // namespace synergy::gui

View File

@ -1,6 +1,6 @@
/*
* synergy -- mouse and keyboard sharing utility
* Copyright (C) 2020 Symless Ltd.
* 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
@ -17,20 +17,10 @@
#pragma once
#include <QString>
namespace synergy::gui {
/// @brief Common configuration interface
class CommonConfig {
public:
CommonConfig() = default;
virtual ~CommonConfig() = default;
virtual void loadSettings() = 0;
virtual void saveSettings() = 0;
bool modified() const { return m_modified; }
void setModified(bool modified) { m_modified = modified; }
private:
bool m_modified = false;
};
QString version();
} // namespace synergy::gui

View File

@ -435,11 +435,13 @@ void Server::switchScreen(
BaseClientProxy *dst, SInt32 x, SInt32 y, bool forScreensaver) {
assert(dst != NULL);
// if trial is expired, exit the process
License license(m_args.m_serialKey);
if (license.isExpired()) {
LOG((CLOG_ERR "trial has expired, aborting server"));
exit(kExitSuccess);
if (m_args.m_serialKey.isValid) {
// if license is expired, exit the process
License license(m_args.m_serialKey);
if (license.isExpired()) {
LOG((CLOG_ERR "trial has expired, aborting server"));
exit(kExitSuccess);
}
}
#ifndef NDEBUG

View File

@ -17,6 +17,8 @@
#pragma once
#include "gui/messages.h"
#include <QCoreApplication>
#include <gtest/gtest.h>
@ -26,6 +28,7 @@ public:
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(); }

View File

@ -17,6 +17,8 @@
#pragma once
#include "gui/messages.h"
#include <QApplication>
#include <gtest/gtest.h>
@ -26,6 +28,7 @@ public:
char **argv = nullptr;
int argc = 0;
s_app = std::make_unique<QApplication>(argc, argv);
qInstallMessageHandler(synergy::gui::messages::messageHandler);
}
static void TearDownTestSuite() { s_app.reset(); }

View File

@ -33,6 +33,8 @@ using PollFD = struct pollfd[];
namespace {
struct MockDeps : public ArchNetworkBSD::Deps {
std::shared_ptr<PollFD> m_pollFD;
MockDeps() {
ON_CALL(*this, makePollFD(_)).WillByDefault([this](nfds_t n) {
m_pollFD = ArchNetworkBSD::Deps::makePollFD(n);
@ -40,29 +42,31 @@ struct MockDeps : public ArchNetworkBSD::Deps {
});
}
static std::shared_ptr<NiceMock<MockDeps>> makeNice() {
return std::make_shared<NiceMock<MockDeps>>();
}
MOCK_METHOD(void, sleep, (double), (override));
MOCK_METHOD(int, poll, (struct pollfd *, nfds_t, int), (override));
MOCK_METHOD(std::shared_ptr<PollFD>, makePollFD, (nfds_t), (override));
MOCK_METHOD(ssize_t, read, (int, void *, size_t), (override));
MOCK_METHOD(void, testCancelThread, (), (override));
std::shared_ptr<PollFD> m_pollFD;
};
} // namespace
TEST(ArchNetworkBSDTests, pollSocket_zeroEntries_callsSleep) {
MockDeps deps;
auto deps = MockDeps::makeNice();
ArchNetworkBSD networkBSD(deps);
EXPECT_CALL(deps, sleep(1)).Times(1);
EXPECT_CALL(*deps, sleep(1)).Times(1);
auto result = networkBSD.pollSocket(nullptr, 0, 1);
EXPECT_EQ(result, 0);
}
TEST(ArchNetworkBSDTests, pollSocket_mockAccessError_throws) {
NiceMock<MockDeps> deps;
ON_CALL(deps, poll(_, _, _)).WillByDefault([]() {
auto deps = MockDeps::makeNice();
ON_CALL(*deps, poll(_, _, _)).WillByDefault([]() {
errno = EACCES;
return -1;
});
@ -77,8 +81,8 @@ TEST(ArchNetworkBSDTests, pollSocket_mockAccessError_throws) {
}
TEST(ArchNetworkBSDTests, pollSocket_pfdHasRevents_copiedToEntries) {
NiceMock<MockDeps> deps;
ON_CALL(deps, poll(_, _, _)).WillByDefault([](auto pfd, auto, auto) {
auto deps = MockDeps::makeNice();
ON_CALL(*deps, poll(_, _, _)).WillByDefault([](auto pfd, auto, auto) {
pfd[0].revents = POLLIN | POLLOUT | POLLERR | POLLNVAL;
return 0;
});
@ -93,50 +97,50 @@ TEST(ArchNetworkBSDTests, pollSocket_pfdHasRevents_copiedToEntries) {
}
TEST(ArchNetworkBSDTests, pollSocket_nullSocket_fdIsNegativeOne) {
NiceMock<MockDeps> deps;
auto deps = MockDeps::makeNice();
ArchNetworkBSD networkBSD(deps);
PollEntries entries{{nullptr, 0, 0}};
networkBSD.pollSocket(entries.data(), static_cast<int>(entries.size()), 1);
EXPECT_EQ(deps.m_pollFD[0].fd, -1);
EXPECT_EQ(deps->m_pollFD[0].fd, -1);
}
TEST(ArchNetworkBSDTests, pollSocket_socketSet_fdWasSet) {
NiceMock<MockDeps> deps;
auto deps = MockDeps::makeNice();
ArchNetworkBSD networkBSD(deps);
ArchSocketImpl socket{1, 0};
PollEntries entries{{&socket, 0, 0}};
networkBSD.pollSocket(entries.data(), static_cast<int>(entries.size()), 1);
EXPECT_EQ(deps.m_pollFD[0].fd, 1);
EXPECT_EQ(deps->m_pollFD[0].fd, 1);
}
TEST(ArchNetworkBSDTests, pollSocket_eventHasPollInBit_bitWasSet) {
NiceMock<MockDeps> deps;
auto deps = MockDeps::makeNice();
ArchNetworkBSD networkBSD(deps);
ArchSocketImpl socket{1, 0};
PollEntries entries{{&socket, IArchNetwork::kPOLLIN, 0}};
networkBSD.pollSocket(entries.data(), static_cast<int>(entries.size()), 1);
EXPECT_EQ(deps.m_pollFD[0].events, POLLIN);
EXPECT_EQ(deps->m_pollFD[0].events, POLLIN);
}
TEST(ArchNetworkBSDTests, pollSocket_eventHasPollOutBit_bitWasSet) {
NiceMock<MockDeps> deps;
auto deps = MockDeps::makeNice();
ArchNetworkBSD networkBSD(deps);
ArchSocketImpl socket{1, 0};
PollEntries entries{{&socket, IArchNetwork::kPOLLOUT, 0}};
networkBSD.pollSocket(entries.data(), static_cast<int>(entries.size()), 1);
EXPECT_EQ(deps.m_pollFD[0].events, POLLOUT);
EXPECT_EQ(deps->m_pollFD[0].events, POLLOUT);
}
TEST(ArchNetworkBSDTests, pollSocket_nullSocket_unblockPipeAppended) {
NiceMock<MockDeps> deps;
auto deps = MockDeps::makeNice();
ArchNetworkBSD networkBSD(deps);
PollEntries entries{{nullptr, 0, 0}};
@ -144,42 +148,42 @@ TEST(ArchNetworkBSDTests, pollSocket_nullSocket_unblockPipeAppended) {
// interesting: unblock pipe fd comes from `getNetworkDataForThread` which
// seems to differ depending on linux distro.
EXPECT_GT(deps.m_pollFD[1].fd, -1);
EXPECT_GT(deps->m_pollFD[1].fd, -1);
}
TEST(ArchNetworkBSDTests, pollSocket_unblockPipeReventsError_readCalled) {
const auto unblockPipeIndex = 1;
NiceMock<MockDeps> deps;
ON_CALL(deps, poll(_, _, _)).WillByDefault([](auto pfd, auto, auto) {
auto deps = MockDeps::makeNice();
ON_CALL(*deps, poll(_, _, _)).WillByDefault([](auto pfd, auto, auto) {
pfd[unblockPipeIndex].revents = POLLIN;
return 1;
});
ON_CALL(deps, read(_, _, _)).WillByDefault([]() {
ON_CALL(*deps, read(_, _, _)).WillByDefault([]() {
errno = EAGAIN;
return 0;
});
ArchNetworkBSD networkBSD(deps);
PollEntries entries{{nullptr, 0, 0}};
EXPECT_CALL(deps, read(_, _, _)).Times(1);
EXPECT_CALL(*deps, read(_, _, _)).Times(1);
networkBSD.pollSocket(entries.data(), static_cast<int>(entries.size()), 1);
}
TEST(ArchNetworkBSDTests, pollSocket_interruptSystemCall_testCancelThread) {
NiceMock<MockDeps> deps;
ON_CALL(deps, poll(_, _, _)).WillByDefault([]() {
auto deps = MockDeps::makeNice();
ON_CALL(*deps, poll(_, _, _)).WillByDefault([]() {
errno = EINTR;
return -1;
});
ArchNetworkBSD networkBSD(deps);
PollEntries entries{{nullptr, 0, 0}};
EXPECT_CALL(deps, testCancelThread()).Times(1);
EXPECT_CALL(*deps, testCancelThread()).Times(1);
networkBSD.pollSocket(entries.data(), static_cast<int>(entries.size()), 1);
}
TEST(ArchNetworkBSDTests, isAnyAddr_goodAddress_returnsTrue) {
MockDeps deps;
auto deps = MockDeps::makeNice();
ArchNetworkBSD networkBSD(deps);
std::unique_ptr<ArchNetAddressImpl> addr;
addr.reset(networkBSD.newAnyAddr(IArchNetwork::kINET6));
@ -190,7 +194,7 @@ TEST(ArchNetworkBSDTests, isAnyAddr_goodAddress_returnsTrue) {
}
TEST(ArchNetworkBSDTests, isAnyAddr_badAddress_returnsFalse) {
MockDeps deps;
auto deps = MockDeps::makeNice();
ArchNetworkBSD networkBSD(deps);
std::unique_ptr<ArchNetAddressImpl> addr;
addr.reset(networkBSD.newAnyAddr(IArchNetwork::kINET6));

View File

@ -27,37 +27,35 @@ namespace {
class MockScopes : public synergy::gui::IConfigScopes {
public:
MOCK_METHOD(void, signalReady, (), (override));
MOCK_METHOD(
void, registerReceiver, (synergy::gui::CommonConfig * receiver),
(override));
MOCK_METHOD(void, loadAll, (), (override));
bool, scopeContains, (const QString &name, Scope scope),
(const, override));
MOCK_METHOD(
bool, hasSetting, (const QString &name, Scope scope), (const, override));
MOCK_METHOD(
QVariant, loadSetting,
QVariant, getFromScope,
(const QString &name, const QVariant &defaultValue, Scope scope),
(const, override));
MOCK_METHOD(
void, setSetting,
void, setInScope,
(const QString &name, const QVariant &value, Scope scope), (override));
MOCK_METHOD(Scope, getScope, (), (const, override));
MOCK_METHOD(void, setScope, (Scope scope), (override));
MOCK_METHOD(bool, isWritable, (), (const, override));
MOCK_METHOD(QSettings *, currentSettings, (), (const, override));
MOCK_METHOD(void, saveAll, (), (override));
MOCK_METHOD(Scope, activeScope, (), (const, override));
MOCK_METHOD(void, setActiveScope, (Scope scope), (override));
MOCK_METHOD(bool, isActiveScopeWritable, (), (const, override));
MOCK_METHOD(QSettings *, activeSettings, (), (const, override));
MOCK_METHOD(void, save, (), (override));
};
struct MockDeps : public AppConfig::Deps {
NiceMock<MockScopes> m_scopes;
MockDeps() {
ON_CALL(*this, profileDir()).WillByDefault(Return("stub"));
ON_CALL(*this, scopes()).WillByDefault(ReturnRef(m_scopes));
ON_CALL(*this, hostname()).WillByDefault(Return("stub"));
}
static std::shared_ptr<NiceMock<MockDeps>> makeNice() {
return std::make_shared<NiceMock<MockDeps>>();
}
MOCK_METHOD(QString, profileDir, (), (const, override));
MOCK_METHOD(synergy::gui::IConfigScopes &, scopes, (), (override));
MOCK_METHOD(QString, hostname, (), (const, override));
};
@ -66,43 +64,36 @@ struct MockDeps : public AppConfig::Deps {
class AppConfigTests : public Test {};
TEST_F(AppConfigTests, ctor_byDefault_screenNameIsHostname) {
NiceMock<MockDeps> deps;
ON_CALL(deps, hostname()).WillByDefault(Return("test"));
NiceMock<MockScopes> scopes;
auto deps = MockDeps::makeNice();
ON_CALL(*deps, hostname()).WillByDefault(Return("test hostname"));
AppConfig appConfig(deps);
AppConfig appConfig(scopes, deps);
ASSERT_EQ(appConfig.screenName().toStdString(), "test");
ASSERT_EQ(appConfig.screenName().toStdString(), "test hostname");
}
TEST_F(AppConfigTests, loadAllScopes_byDefault_callsScopesLoadAll) {
NiceMock<MockDeps> deps;
AppConfig appConfig(deps);
TEST_F(AppConfigTests, ctor_byDefault_getsFromScope) {
NiceMock<MockScopes> scopes;
auto deps = MockDeps::makeNice();
EXPECT_CALL(deps.m_scopes, loadAll());
ON_CALL(scopes, scopeContains(_, _)).WillByDefault(Return(true));
ON_CALL(scopes, getFromScope(_, _, _))
.WillByDefault(Return(QVariant("test screen")));
EXPECT_CALL(scopes, getFromScope(_, _, _)).Times(AnyNumber());
appConfig.loadAllScopes();
AppConfig appConfig(scopes, deps);
ASSERT_EQ(appConfig.screenName().toStdString(), "test screen");
}
TEST_F(AppConfigTests, loadSettings_byDefault_callsScopesLoadSetting) {
NiceMock<MockDeps> deps;
AppConfig appConfig(deps);
TEST_F(AppConfigTests, commit_byDefault_setsToScope) {
NiceMock<MockScopes> scopes;
auto deps = MockDeps::makeNice();
AppConfig appConfig(scopes, deps);
ON_CALL(deps.m_scopes, hasSetting(_, _)).WillByDefault(Return(true));
ON_CALL(deps.m_scopes, loadSetting(_, _, _))
.WillByDefault(Return(QVariant("test")));
EXPECT_CALL(deps.m_scopes, loadSetting(_, _, _)).Times(AnyNumber());
ON_CALL(scopes, isActiveScopeWritable()).WillByDefault(Return(true));
EXPECT_CALL(scopes, setInScope(_, _, _)).Times(AnyNumber());
appConfig.loadSettings();
ASSERT_EQ(appConfig.screenName().toStdString(), "test");
}
TEST_F(AppConfigTests, saveSettings_byDefault_callsScopesSetSetting) {
NiceMock<MockDeps> deps;
AppConfig appConfig(deps);
ON_CALL(deps.m_scopes, isWritable()).WillByDefault(Return(true));
EXPECT_CALL(deps.m_scopes, setSetting(_, _, _)).Times(AnyNumber());
appConfig.saveSettings();
appConfig.commit();
}

View File

@ -28,6 +28,7 @@ const auto kFuture = system_clock::now() + hours(1);
TEST(LicenseHandlerTests, changeSerialKey_validExpiredLicense_returnsTrue) {
LicenseHandler licenseHandler;
licenseHandler.setEnabled(true);
auto hexString = //
"7B76313B70726F3B6E69636B20626F6C746F6E3B313B6"
"E69636B4073796D6C6573732E636F6D3B203B303B307D";