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
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
50 changed files with 1525 additions and 1186 deletions

View file

@ -27,6 +27,7 @@ env:
SYNERGY_PRODUCT_NAME: ${{ vars.SYNERGY_PRODUCT_NAME }} SYNERGY_PRODUCT_NAME: ${{ vars.SYNERGY_PRODUCT_NAME }}
SYNERGY_PACKAGE_PREFIX: ${{ vars.SYNERGY_PACKAGE_PREFIX }} SYNERGY_PACKAGE_PREFIX: ${{ vars.SYNERGY_PACKAGE_PREFIX }}
SYNERGY_ENABLE_LICENSING: ${{ vars.SYNERGY_ENABLE_LICENSING }} SYNERGY_ENABLE_LICENSING: ${{ vars.SYNERGY_ENABLE_LICENSING }}
SYNERGY_SHOW_DEV_THANKS: ${{ vars.SYNERGY_SHOW_DEV_THANKS }}
PACKAGE_BUILD: ${{ !github.event.pull_request.draft }} PACKAGE_BUILD: ${{ !github.event.pull_request.draft }}
PACKAGE_UPLOAD: ${{ !github.event.pull_request.draft && github.event_name != 'schedule' }} PACKAGE_UPLOAD: ${{ !github.event.pull_request.draft && github.event_name != 'schedule' }}
UPLOAD_TO_GITHUB: ${{ github.event_name == 'pull_request' && !github.event.pull_request.draft }} 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 - #7421 Only load core after settings have fully loaded
- #7419 Introduce 'Advanced' tab to Preferences window - #7419 Introduce 'Advanced' tab to Preferences window
- #7422 Handle empty value for computer name setting - #7422 Handle empty value for computer name setting
- #7423 Refactor local/global save/load and show dev thanks message
# 1.14.6 # 1.14.6

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -24,18 +24,4 @@
#include <QtGui> #include <QtGui>
QSynergyApplication::QSynergyApplication(int &argc, char **argv) QSynergyApplication::QSynergyApplication(int &argc, char **argv)
: QApplication(argc, 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();
}
}

View file

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

View file

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

View file

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

View file

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

View file

@ -332,7 +332,7 @@
</widget> </widget>
<widget class="QWidget" name="m_pTabAdvanced"> <widget class="QWidget" name="m_pTabAdvanced">
<attribute name="title"> <attribute name="title">
<string>Advanced server settings</string> <string>Advanced</string>
</attribute> </attribute>
<layout class="QGridLayout"> <layout class="QGridLayout">
<property name="leftMargin"> <property name="leftMargin">
@ -839,289 +839,284 @@
</sizepolicy> </sizepolicy>
</property> </property>
<attribute name="title"> <attribute name="title">
<string>Advanced config</string> <string>Config file</string>
</attribute> </attribute>
<layout class="QVBoxLayout" name="verticalLayout_2"> <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> <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> <item>
<spacer name="verticalSpacer_2"> <spacer name="horizontalSpacer_2">
<property name="orientation"> <property name="orientation">
<enum>Qt::Vertical</enum> <enum>Qt::Horizontal</enum>
</property>
<property name="sizeType">
<enum>QSizePolicy::Expanding</enum>
</property> </property>
<property name="sizeHint" stdset="0"> <property name="sizeHint" stdset="0">
<size> <size>
<width>20</width> <width>40</width>
<height>40</height> <height>20</height>
</size> </size>
</property> </property>
</spacer> </spacer>
</item> </item>
<item> <item>
<widget class="QLabel" name="label_5"> <widget class="QCheckBox" name="m_pCheckBoxUseExternalConfig">
<property name="text"> <property name="sizePolicy">
<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> <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property> </property>
<property name="textFormat"> <property name="minimumSize">
<enum>Qt::RichText</enum> <size>
<width>300</width>
<height>0</height>
</size>
</property>
<property name="text">
<string>Use Core server config file</string>
</property> </property>
</widget> </widget>
</item> </item>
<item> <item>
<spacer name="verticalSpacer_3"> <spacer name="horizontalSpacer_3">
<property name="orientation"> <property name="orientation">
<enum>Qt::Vertical</enum> <enum>Qt::Horizontal</enum>
</property> </property>
<property name="sizeType"> <property name="sizeType">
<enum>QSizePolicy::Minimum</enum> <enum>QSizePolicy::Expanding</enum>
</property> </property>
<property name="sizeHint" stdset="0"> <property name="sizeHint" stdset="0">
<size> <size>
<width>20</width> <width>40</width>
<height>5</height> <height>20</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>
</size> </size>
</property> </property>
</spacer> </spacer>
</item> </item>
</layout> </layout>
</item> </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> </layout>
</widget> </widget>
</widget> </widget>

View file

@ -69,10 +69,10 @@ void ServerConnection::addClient(const QString &clientName) {
} }
void ServerConnection::configureClient(const QString &clientName) { void ServerConnection::configureClient(const QString &clientName) {
ServerConfigDialog dlg( ServerConfigDialog dialog(
&m_parent, m_parent.serverConfig(), m_parent.appConfig()); &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(); m_parent.restartCore();
} }
} }

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -21,9 +21,16 @@
#include "SetupWizard.h" #include "SetupWizard.h"
#include "SetupWizardBlocker.h" #include "SetupWizardBlocker.h"
#include "gui/AppConfig.h" #include "gui/AppConfig.h"
#include "gui/ConfigScopes.h"
#include "gui/constants.h"
#include "gui/dotenv.h" #include "gui/dotenv.h"
#include "gui/messages.h"
#include "gui/version.h"
#include <QApplication>
#include <QDebug>
#include <QMessageBox> #include <QMessageBox>
#include <QObject>
#include <QtCore> #include <QtCore>
#include <QtGui> #include <QtGui>
@ -46,19 +53,23 @@ bool checkMacAssistiveDevices();
#endif #endif
int main(int argc, char *argv[]) { int main(int argc, char *argv[]) {
#ifdef Q_OS_DARWIN #ifdef Q_OS_DARWIN
/* Workaround for QTBUG-40332 - "High ping when QNetworkAccessManager is /* Workaround for QTBUG-40332 - "High ping when QNetworkAccessManager is
* instantiated" */ * instantiated" */
::setenv("QT_BEARER_POLL_TIMEOUT", "-1", 1); ::setenv("QT_BEARER_POLL_TIMEOUT", "-1", 1);
#endif #endif
dotenv(".env"); QCoreApplication::setOrganizationName(kAppName);
QCoreApplication::setApplicationName(kAppName);
QCoreApplication::setOrganizationName("Synergy"); QCoreApplication::setOrganizationDomain(kAppDomain);
QCoreApplication::setOrganizationDomain("http://symless.com/");
QCoreApplication::setApplicationName("Synergy");
QSynergyApplication app(argc, argv); 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) #if defined(Q_OS_MAC)
@ -75,31 +86,41 @@ int main(int argc, char *argv[]) {
} }
#endif #endif
AppConfig appConfig;
qRegisterMetaType<Edition>("Edition"); 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( QObject::connect(
&app, &QSynergyApplication::aboutToQuit, &mainWindow, &app, &QSynergyApplication::aboutToQuit, &mainWindow,
&MainWindow::onAppAboutToQuit); &MainWindow::onAppAboutToQuit);
std::unique_ptr<SetupWizardBlocker> setupBlocker; mainWindow.open();
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();
}
return QSynergyApplication::exec(); return QSynergyApplication::exec();
} }

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -17,58 +17,81 @@
#include "ConfigScopes.h" #include "ConfigScopes.h"
#include "CommonConfig.h"
#include <QCoreApplication> #include <QCoreApplication>
#include <QFile> #include <QFile>
#include <cassert> #include <cassert>
#include <memory> #include <memory>
const auto kSystemConfigFilename = "SystemConfig.ini";
const auto kUnixSystemConfigPath = "/usr/local/etc/symless/";
namespace synergy::gui { namespace synergy::gui {
QString getSystemSettingPath() { QString getSystemSettingPath() {
const QString settingFilename("SystemConfig.ini"); const QString settingFilename(kSystemConfigFilename);
QString path;
#if defined(Q_OS_WIN) #if defined(Q_OS_WIN)
path = QCoreApplication::applicationDirPath() + "\\"; return QCoreApplication::applicationDirPath() + "\\";
#elif defined(Q_OS_DARWIN) #elif defined(Q_OS_DARWIN)
// Global preferances dir // it would be nice to use /Library dir, but qt has no elevate system.
// Would be nice to use /library, but QT has no elevate system in place return kUnixSystemConfigPath + settingFilename;
path = "/usr/local/etc/symless/";
#elif defined(Q_OS_LINUX) #elif defined(Q_OS_LINUX)
// QT adds application and filename to the end of the path already on linux // qt already adds application and filename to the end of the path on linux.
path = "/usr/local/etc/symless/"; return kUnixSystemConfigPath;
return path;
#else #else
assert("OS not supported"); qFatal("unsupported platform");
return "";
#endif #endif
return path + settingFilename;
} }
#if defined(Q_OS_WIN) #if defined(Q_OS_WIN)
void loadWindowsLegacy(QSettings &settings) { void loadWindowsLegacy(QSettings &settings) {
if (!QFile(settings.fileName()).exists()) { if (QFile(settings.fileName()).exists()) {
QSettings::setPath( qDebug("system settings already exist, skipping legacy load");
QSettings::IniFormat, QSettings::SystemScope, "SystemConfig.ini"); return;
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());
} }
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 #endif
ConfigScopes::ConfigScopes() { 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::setPath(
QSettings::Format::IniFormat, QSettings::Scope::SystemScope, QSettings::Format::IniFormat, QSettings::Scope::SystemScope,
getSystemSettingPath()); getSystemSettingPath());
@ -76,47 +99,57 @@ ConfigScopes::ConfigScopes() {
// Config will default to User settings if they exist, // Config will default to User settings if they exist,
// otherwise it will load System setting and save them to User settings // otherwise it will load System setting and save them to User settings
m_pSystemSettings = std::make_unique<QSettings>( m_pSystemSettings = std::make_unique<QSettings>(
QSettings::Format::IniFormat, QSettings::Scope::SystemScope, QSettings::Format::IniFormat, QSettings::Scope::SystemScope, orgName,
QCoreApplication::organizationName(), appName);
QCoreApplication::applicationName());
// default to user scope. qDebug() << "system settings path:" << m_pSystemSettings->fileName();
// 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>();
load();
}
ConfigScopes::~ConfigScopes() {
while (!m_pReceivers.empty()) {
m_pReceivers.pop_back();
}
}
void ConfigScopes::load() {
#if defined(Q_OS_WIN) #if defined(Q_OS_WIN)
// This call is needed for backwardcapability with old settings.
loadWindowsLegacy(*m_pSystemSettings); loadWindowsLegacy(*m_pSystemSettings);
#endif #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) { switch (scope) {
case Scope::User: case Scope::User:
return m_pUserSettings->contains(name); return m_pUserSettings->contains(name);
case Scope::System: case Scope::System:
return m_pSystemSettings->contains(name); return m_pSystemSettings->contains(name);
default: default:
return currentSettings()->contains(name); return activeSettings()->contains(name);
} }
} }
bool ConfigScopes::isWritable() const { QSettings *ConfigScopes::activeSettings() const {
return currentSettings()->isWritable(); 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 { const QString &name, const QVariant &defaultValue, Scope scope) const {
switch (scope) { switch (scope) {
case Scope::User: case Scope::User:
@ -124,68 +157,23 @@ QVariant ConfigScopes::loadSetting(
case Scope::System: case Scope::System:
return m_pSystemSettings->value(name, defaultValue); return m_pSystemSettings->value(name, defaultValue);
default: default:
return currentSettings()->value(name, defaultValue); return activeSettings()->value(name, defaultValue);
} }
} }
void ConfigScopes::setScope(ConfigScopes::Scope scope) { void ConfigScopes::setInScope(
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(
const QString &name, const QVariant &value, Scope scope) { const QString &name, const QVariant &value, Scope scope) {
currentSettings()->setValue(name, value); switch (scope) {
m_unsavedChanges = true; 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 } // namespace synergy::gui

View file

@ -19,94 +19,44 @@
#include "IConfigScopes.h" #include "IConfigScopes.h"
#include <QObject>
#include <QSettings> #include <QSettings>
#include <QVariant> #include <QVariant>
#include <memory> #include <memory>
namespace synergy::gui { namespace synergy::gui {
class CommonConfig; /// @brief Encapsulates Qt config for both user and global scopes.
class ConfigScopes : public QObject, public IConfigScopes {
/// @brief A general config reader and writer for user and gloabl settings Q_OBJECT
class ConfigScopes : public IConfigScopes {
public: public:
explicit ConfigScopes(); explicit ConfigScopes();
virtual ~ConfigScopes(); virtual ~ConfigScopes() = default;
/// @brief Checks if the setting exists void signalReady() override;
/// @param [in] name The name of the setting to check void save() override;
/// @param [in] scope The scope to search in bool scopeContains(
/// @return bool True if the current scope has the named setting const QString &name, Scope scope = Scope::Current) const override;
bool bool isActiveScopeWritable() const override;
hasSetting(const QString &name, Scope scope = Scope::Current) const override; void setInScope(
/// @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(
const QString &name, const QVariant &value, const QString &name, const QVariant &value,
Scope scope = Scope::Current) override; Scope scope = Scope::Current) override;
QVariant getFromScope(
/// @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(
const QString &name, const QVariant &defaultValue = QVariant(), const QString &name, const QVariant &defaultValue = QVariant(),
Scope scope = Scope::Current) const override; 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 signals:
/// scope void ready();
/// @param [in] scope The scope to set void saving();
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;
private: private:
void load(); Scope m_currentScope = Scope::User;
Scope m_CurrentScope = Scope::User;
std::unique_ptr<QSettings> m_pUserSettings; std::unique_ptr<QSettings> m_pUserSettings;
std::unique_ptr<QSettings> m_pSystemSettings; std::unique_ptr<QSettings> m_pSystemSettings;
/// @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 } // namespace synergy::gui

View file

@ -17,8 +17,6 @@
#pragma once #pragma once
#include "gui/CommonConfig.h"
#include <QSettings> #include <QSettings>
#include <QString> #include <QString>
#include <QVariant> #include <QVariant>
@ -31,21 +29,42 @@ public:
virtual ~IConfigScopes() = default; virtual ~IConfigScopes() = default;
virtual void registerReceiver(CommonConfig *receiver) = 0; virtual Scope activeScope() const = 0;
virtual void loadAll() = 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 virtual bool
hasSetting(const QString &name, Scope scope = Scope::Current) const = 0; scopeContains(const QString &name, Scope scope = Scope::Current) const = 0;
virtual QVariant loadSetting(
/**
* @brief Load a config value from a scope (default is current scope).
*/
virtual QVariant getFromScope(
const QString &name, const QVariant &defaultValue = QVariant(), const QString &name, const QVariant &defaultValue = QVariant(),
Scope scope = Scope::Current) const = 0; 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, const QString &name, const QVariant &value,
Scope scope = Scope::Current) = 0; 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 } // namespace synergy::gui

View file

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

View file

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

View file

@ -56,13 +56,13 @@ QIpcClient::~QIpcClient() {
void QIpcClient::connected() { void QIpcClient::connected() {
sendHello(); sendHello();
infoMessage("connection established"); emit infoMessage("connection established");
} }
void QIpcClient::connectToHost() { void QIpcClient::connectToHost() {
m_Enabled = true; m_Enabled = true;
infoMessage("connecting to service..."); emit infoMessage("connecting to service...");
const auto port = static_cast<quint16>(kIpcPort); const auto port = static_cast<quint16>(kIpcPort);
m_Socket->connectToHost(QHostAddress(QHostAddress::LocalHost), port); m_Socket->connectToHost(QHostAddress(QHostAddress::LocalHost), port);
@ -73,7 +73,7 @@ void QIpcClient::connectToHost() {
} }
void QIpcClient::disconnectFromHost() { void QIpcClient::disconnectFromHost() {
infoMessage("service disconnect"); emit infoMessage("service disconnect");
m_Reader->stop(); m_Reader->stop();
m_Socket->close(); m_Socket->close();
} }
@ -92,7 +92,7 @@ void QIpcClient::error(QAbstractSocket::SocketError error) {
break; break;
} }
errorMessage(QString("ipc connection error, %1").arg(text)); emit errorMessage(QString("ipc connection error, %1").arg(text));
QTimer::singleShot(1000, this, SLOT(retryConnect())); QTimer::singleShot(1000, this, SLOT(retryConnect()));
} }
@ -133,7 +133,9 @@ void QIpcClient::sendCommand(
stream->writeRawData(elevateBuf, 1); 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. // TODO: qt must have a built in way of converting int to bytes.
void QIpcClient::intToBytes(int value, char *buffer, int size) { 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 { void TlsUtility::generateCertificate(bool replace) const {
qDebug("generating tls certificate, "
"all clients must trust the new fingerprint");
if (!isAvailableAndEnabled()) { if (!isAvailableAndEnabled()) {
qFatal("unable to generate tls certificate, " qFatal("unable to generate tls certificate, "
"tls is either not available or not enabled"); "tls is either not available or not enabled");

View file

@ -19,25 +19,33 @@
#include <QString> #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 #ifdef SYNERGY_ENABLE_LICENSING
const bool kLicensingEnabled = true; const bool kLicensingEnabled = true;
#else #else
const bool kLicensingEnabled = false; const bool kLicensingEnabled = false;
#endif // SYNERGY_ENABLE_LICENSING #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 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 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 kLinkDownload = R"(<a href="%1" style="color: %2">Download now</a>)";
const auto kUrlSourceQuery = "source=gui"; const auto kUrlSourceQuery = "source=gui";
const auto kUrlWebsite = "https://symless.com"; 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 kUrlProduct = QString("%1/synergy").arg(kUrlWebsite);
const auto kUrlPurchase = const auto kUrlPurchase =
QString("%1/purchase?%2").arg(kUrlProduct, kUrlSourceQuery); 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 kUrlHelp = QString("%1/help?%2").arg(kUrlProduct, kUrlSourceQuery);
const auto kUrlDownload = const auto kUrlDownload =
QString("%1/download?%2").arg(kUrlProduct, kUrlSourceQuery); 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 "constants.h"
#include "license/License.h" #include "license/License.h"
#include "styles.h"
using License = synergy::license::License; 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 * 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 * This package is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License * modify it under the terms of the GNU General Public License
@ -17,20 +17,10 @@
#pragma once #pragma once
#include <QString>
namespace synergy::gui { namespace synergy::gui {
/// @brief Common configuration interface QString version();
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;
};
} // namespace synergy::gui } // namespace synergy::gui

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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