Show message box explaining why settings are read-only (#7437)

* Swap assert for warning log lines

* Flush on IPC write

* Flush on cleanup instead of write

* Record core started setting

* Show server first start message

* Show message when active scope is read-only

* Show read only message on change

* Show read only on window show

* Try to improve main window size policy

* Revert addition of resizer

* Remove redundant file path fiddling

* Remove dead code and fixed missing const

* Print path and use queued connection

* Improve read-only message on Windows

* Only show toggle warning when dialog visible

* Update ChangeLog

* Fixed include
This commit is contained in:
Nick Bolton 2024-08-12 16:39:18 +01:00 committed by GitHub
parent 30ea95fb3d
commit 9464aa46b8
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
25 changed files with 277 additions and 106 deletions

View file

@ -79,6 +79,7 @@ Enhancements:
- #7434 Show dark logo in dark mode and improve .env loader - #7434 Show dark logo in dark mode and improve .env loader
- #7435 Add reset settings menu action and env var - #7435 Add reset settings menu action and env var
- #7436 Introduced new env vars for testing - #7436 Introduced new env vars for testing
- #7437 Show message box explaining why settings are read-only
# 1.14.6 # 1.14.6

View file

@ -799,7 +799,7 @@ void MainWindow::closeEvent(QCloseEvent *event) {
qDebug("window should hide to tray"); qDebug("window should hide to tray");
} }
void MainWindow::showFirstRunMessage() { void MainWindow::showFirstConnectedMessage() {
if (m_AppConfig.startedBefore()) { if (m_AppConfig.startedBefore()) {
return; return;
} }
@ -808,7 +808,7 @@ void MainWindow::showFirstRunMessage() {
m_ConfigScopes.save(); m_ConfigScopes.save();
const auto isServer = m_CoreProcess.mode() == CoreMode::Server; const auto isServer = m_CoreProcess.mode() == CoreMode::Server;
messages::showFirstRunMessage( messages::showFirstConnectedMessage(
this, m_AppConfig.closeToTray(), m_AppConfig.enableService(), isServer); this, m_AppConfig.closeToTray(), m_AppConfig.enableService(), isServer);
} }
@ -887,10 +887,14 @@ void MainWindow::updateStatus() {
} }
void MainWindow::onCoreProcessStateChanged(CoreProcessState state) { void MainWindow::onCoreProcessStateChanged(CoreProcessState state) {
qDebug("core process state changed: %d", static_cast<int>(state));
updateStatus(); updateStatus();
if (state == CoreProcessState::Started) {
qDebug("recording that core has started");
m_AppConfig.setStartedBefore(true);
m_ConfigScopes.save();
}
if (state == CoreProcessState::Started || if (state == CoreProcessState::Started ||
state == CoreProcessState::Starting) { state == CoreProcessState::Starting) {
disconnect( disconnect(
@ -933,7 +937,7 @@ void MainWindow::onCoreConnectionStateChanged(CoreConnectionState state) {
if (state != CoreConnectionState::Connected) { if (state != CoreConnectionState::Connected) {
secureSocket(false); secureSocket(false);
} else if (isVisible()) { } else if (isVisible()) {
showFirstRunMessage(); showFirstConnectedMessage();
showDevThanksMessage(); showDevThanksMessage();
} }
} }
@ -1127,12 +1131,21 @@ void MainWindow::enableServer(bool enable) {
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_pWidgetServerInput->setVisible(m_AppConfig.invertConnection());
if (enable) { if (enable) {
m_pButtonToggleStart->setEnabled(true); m_pButtonToggleStart->setEnabled(true);
m_pActionStartCore->setEnabled(true); m_pActionStartCore->setEnabled(true);
m_CoreProcess.setMode(CoreProcess::Mode::Server); m_CoreProcess.setMode(CoreProcess::Mode::Server);
// The server can run without any clients configured, and this is actually
// what you'll want to do the first time since you'll be prompted when an
// unrecognized client tries to connect.
if (!m_AppConfig.startedBefore()) {
qDebug("auto-starting core server for first time");
m_CoreProcess.start();
messages::showFirstServerStartMessage(this);
}
} }
} }
@ -1140,8 +1153,8 @@ void MainWindow::enableClient(bool enable) {
qDebug(enable ? "client enabled" : "client disabled"); 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_pWidgetClientInput->setEnabled(enable);
m_pWidgetClient->setVisible(!m_AppConfig.invertConnection()); m_pWidgetClientInput->setVisible(!m_AppConfig.invertConnection());
if (enable) { if (enable) {
m_pButtonToggleStart->setEnabled(true); m_pButtonToggleStart->setEnabled(true);

View file

@ -176,7 +176,7 @@ 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 showFirstConnectedMessage();
void showDevThanksMessage(); void showDevThanksMessage();
QString productName() const; QString productName() const;
void updateStatus(); void updateStatus();

View file

@ -110,29 +110,49 @@
<property name="spacing"> <property name="spacing">
<number>15</number> <number>15</number>
</property> </property>
<item> <item alignment="Qt::AlignTop">
<layout class="QVBoxLayout" name="verticalLayout_3"> <widget class="QWidget" name="m_pWidgetServerRadio" native="true">
<property name="spacing"> <property name="sizePolicy">
<number>0</number> <sizepolicy hsizetype="Preferred" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property> </property>
<item> <layout class="QVBoxLayout" name="verticalLayout_3">
<widget class="QRadioButton" name="m_pRadioGroupServer"> <property name="spacing">
<property name="text"> <number>0</number>
<string>Use this computer's keyboard and mouse</string> </property>
</property> <property name="leftMargin">
</widget> <number>0</number>
</item> </property>
<item> <property name="topMargin">
<widget class="QLabel" name="label"> <number>0</number>
<property name="text"> </property>
<string>(make this computer the server)</string> <property name="rightMargin">
</property> <number>0</number>
<property name="indent"> </property>
<number>20</number> <property name="bottomMargin">
</property> <number>0</number>
</widget> </property>
</item> <item>
</layout> <widget class="QRadioButton" name="m_pRadioGroupServer">
<property name="text">
<string>Use this computer's keyboard and mouse</string>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="label">
<property name="text">
<string>(make this computer the server)</string>
</property>
<property name="indent">
<number>20</number>
</property>
</widget>
</item>
</layout>
</widget>
</item> </item>
<item alignment="Qt::AlignTop"> <item alignment="Qt::AlignTop">
<widget class="QWidget" name="m_pWidgetServer" native="true"> <widget class="QWidget" name="m_pWidgetServer" native="true">
@ -153,7 +173,7 @@
<number>0</number> <number>0</number>
</property> </property>
<item> <item>
<widget class="QWidget" name="m_pWidgetServerInverse" native="true"> <widget class="QWidget" name="m_pWidgetServerInput" native="true">
<layout class="QVBoxLayout" name="m_pLayoutServerInverse"> <layout class="QVBoxLayout" name="m_pLayoutServerInverse">
<property name="spacing"> <property name="spacing">
<number>15</number> <number>15</number>
@ -231,7 +251,7 @@
<property name="sizeHint" stdset="0"> <property name="sizeHint" stdset="0">
<size> <size>
<width>20</width> <width>20</width>
<height>10</height> <height>1</height>
</size> </size>
</property> </property>
</spacer> </spacer>
@ -278,32 +298,52 @@
<property name="spacing"> <property name="spacing">
<number>15</number> <number>15</number>
</property> </property>
<item> <item alignment="Qt::AlignTop">
<layout class="QVBoxLayout" name="verticalLayout_6"> <widget class="QWidget" name="m_pWidgetClientRadio" native="true">
<property name="spacing"> <property name="sizePolicy">
<number>0</number> <sizepolicy hsizetype="Preferred" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property> </property>
<item> <layout class="QVBoxLayout" name="verticalLayout_6">
<widget class="QRadioButton" name="m_pRadioGroupClient"> <property name="spacing">
<property name="text"> <number>0</number>
<string>Use another computer’s mouse and keyboard</string> </property>
</property> <property name="leftMargin">
</widget> <number>0</number>
</item> </property>
<item> <property name="topMargin">
<widget class="QLabel" name="label_2"> <number>0</number>
<property name="text"> </property>
<string>(make this computer the client)</string> <property name="rightMargin">
</property> <number>0</number>
<property name="indent"> </property>
<number>20</number> <property name="bottomMargin">
</property> <number>0</number>
</widget> </property>
</item> <item>
</layout> <widget class="QRadioButton" name="m_pRadioGroupClient">
<property name="text">
<string>Use another computer’s mouse and keyboard</string>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="label_2">
<property name="text">
<string>(make this computer the client)</string>
</property>
<property name="indent">
<number>20</number>
</property>
</widget>
</item>
</layout>
</widget>
</item> </item>
<item alignment="Qt::AlignTop"> <item alignment="Qt::AlignTop">
<widget class="QWidget" name="m_pWidgetClient" native="true"> <widget class="QWidget" name="m_pWidgetClientInput" native="true">
<layout class="QVBoxLayout" name="m_pLayoutClient"> <layout class="QVBoxLayout" name="m_pLayoutClient">
<property name="spacing"> <property name="spacing">
<number>15</number> <number>15</number>
@ -378,7 +418,7 @@
<property name="sizeHint" stdset="0"> <property name="sizeHint" stdset="0">
<size> <size>
<width>20</width> <width>20</width>
<height>10</height> <height>1</height>
</size> </size>
</property> </property>
</spacer> </spacer>

View file

@ -50,8 +50,6 @@ public:
static void msleep(unsigned long msecs) { QThread::msleep(msecs); } static void msleep(unsigned long msecs) { QThread::msleep(msecs); }
}; };
QString getSystemSettingPath();
#if defined(Q_OS_MAC) #if defined(Q_OS_MAC)
bool checkMacAssistiveDevices(); bool checkMacAssistiveDevices();
#endif #endif

View file

@ -20,7 +20,7 @@
#include <QByteArray> #include <QByteArray>
#include <QDataStream> #include <QDataStream>
#include <QIODevice> #include <QIODevice>
#include <qlogging.h> #include <QtGlobal>
namespace synergy::gui { namespace synergy::gui {

View file

@ -394,6 +394,8 @@ void AppConfig::loadScope(ConfigScopes::Scope scope) {
m_Scopes.setActiveScope(scope); m_Scopes.setActiveScope(scope);
qDebug("active scope file path: %s", qPrintable(m_Scopes.activeFilePath()));
// only signal ready if there is at least one setting in the required 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. // this prevents the current settings from being set back to default.
if (m_Scopes.scopeContains( if (m_Scopes.scopeContains(
@ -443,7 +445,7 @@ void AppConfig::persistLogDir() const {
// Begin getters // Begin getters
/////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////
IConfigScopes &AppConfig::scopes() { return m_Scopes; } IConfigScopes &AppConfig::scopes() const { return m_Scopes; }
bool AppConfig::activationHasRun() const { return m_ActivationHasRun; } bool AppConfig::activationHasRun() const { return m_ActivationHasRun; }

View file

@ -126,7 +126,6 @@ public:
IConfigScopes &scopes, IConfigScopes &scopes,
std::shared_ptr<Deps> deps = std::make_shared<Deps>()); std::shared_ptr<Deps> deps = std::make_shared<Deps>());
IConfigScopes &scopes();
void determineScope(); void determineScope();
/** /**
@ -139,6 +138,7 @@ public:
// Getters (overrides) // Getters (overrides)
// //
IConfigScopes &scopes() const override;
ProcessMode processMode() const override; ProcessMode processMode() const override;
ElevateMode elevateMode() const override; ElevateMode elevateMode() const override;
bool tlsEnabled() const override; bool tlsEnabled() const override;
@ -189,6 +189,7 @@ public:
// //
// Setters (overrides) // Setters (overrides)
// //
void setScreenName(const QString &s) override; void setScreenName(const QString &s) override;
void setPort(int i) override; void setPort(int i) override;
void setNetworkInterface(const QString &s) override; void setNetworkInterface(const QString &s) override;
@ -211,9 +212,9 @@ public:
// Setters (new methods) // Setters (new methods)
// //
void setStartedBefore(bool b);
void setActivationHasRun(bool value); void setActivationHasRun(bool value);
void setWizardHasRun(); void setWizardHasRun();
void setStartedBefore(bool b);
void setSerialKey(const QString &serialKey); void setSerialKey(const QString &serialKey);
void clearSerialKey(); void clearSerialKey();
void setLicenseNextCheck(unsigned long long); void setLicenseNextCheck(unsigned long long);

View file

@ -59,7 +59,7 @@ public:
Scope activeScope() const override; Scope activeScope() const override;
QSettingsProxy &activeSettings() override; QSettingsProxy &activeSettings() override;
const QSettingsProxy &activeSettings() const override; const QSettingsProxy &activeSettings() const override;
QString activeFilePath() const; QString activeFilePath() const override;
signals: signals:
void ready(); void ready();

View file

@ -19,6 +19,8 @@
#include "ElevateMode.h" #include "ElevateMode.h"
#include "gui/config/IConfigScopes.h"
#include <QString> #include <QString>
namespace synergy::gui { namespace synergy::gui {
@ -26,13 +28,16 @@ namespace synergy::gui {
enum class ProcessMode { kService, kDesktop }; enum class ProcessMode { kService, kDesktop };
class IAppConfig { class IAppConfig {
using IConfigScopes = synergy::gui::IConfigScopes;
public: public:
virtual ~IAppConfig() = default; virtual ~IAppConfig() = default;
// //
// Setters // Getters
// //
virtual IConfigScopes &scopes() const = 0;
virtual QString tlsCertPath() const = 0; virtual QString tlsCertPath() const = 0;
virtual int tlsKeyLength() const = 0; virtual int tlsKeyLength() const = 0;
virtual bool tlsEnabled() const = 0; virtual bool tlsEnabled() const = 0;
@ -64,7 +69,7 @@ public:
virtual bool clientGroupChecked() const = 0; virtual bool clientGroupChecked() const = 0;
// //
// Getters // Setters
// //
virtual void setLoadFromSystemScope(bool loadFromSystemScope) = 0; virtual void setLoadFromSystemScope(bool loadFromSystemScope) = 0;

View file

@ -38,6 +38,7 @@ public:
virtual bool isActiveScopeWritable() const = 0; virtual bool isActiveScopeWritable() const = 0;
virtual QSettingsProxy &activeSettings() = 0; virtual QSettingsProxy &activeSettings() = 0;
virtual const QSettingsProxy &activeSettings() const = 0; virtual const QSettingsProxy &activeSettings() const = 0;
virtual QString activeFilePath() const = 0;
/** /**
* @brief Signals to listeners that the settings that they should read. * @brief Signals to listeners that the settings that they should read.

View file

@ -35,6 +35,7 @@
#include <QRegularExpression> #include <QRegularExpression>
#include <QStandardPaths> #include <QStandardPaths>
#include <QTimer> #include <QTimer>
#include <QtGlobal>
using namespace synergy::license; using namespace synergy::license;
using namespace synergy::gui::license; using namespace synergy::gui::license;
@ -59,7 +60,25 @@ QString processModeToString(ProcessMode mode) {
return "service"; return "service";
default: default:
qFatal("invalid process mode"); qFatal("invalid process mode");
return ""; abort();
}
}
QString processStateToString(CoreProcess::ProcessState state) {
using enum CoreProcess::ProcessState;
switch (state) {
case Starting:
return "starting";
case Started:
return "started";
case Stopping:
return "stopping";
case Stopped:
return "stopped";
default:
qFatal("invalid process state");
abort();
} }
} }
@ -139,8 +158,8 @@ QString CoreProcess::Deps::getProfileRoot() const {
// //
CoreProcess::CoreProcess( CoreProcess::CoreProcess(
IAppConfig &appConfig, IServerConfig &serverConfig, const ILicense &license, const IAppConfig &appConfig, const IServerConfig &serverConfig,
std::shared_ptr<Deps> deps) const ILicense &license, std::shared_ptr<Deps> deps)
: m_appConfig(appConfig), : m_appConfig(appConfig),
m_serverConfig(serverConfig), m_serverConfig(serverConfig),
m_license(license), m_license(license),
@ -175,7 +194,9 @@ void CoreProcess::onIpcClientServiceReady() {
qDebug("service ready, continuing core process stop"); qDebug("service ready, continuing core process stop");
stop(); stop();
} else { } else {
qCritical("service ready, but process state is not starting or stopping"); // This may happen when the IPC connection fails and then reconnects.
qWarning(
"ignoring service ready, process state is not starting or stopping");
} }
} }
@ -644,6 +665,10 @@ void CoreProcess::setProcessState(ProcessState state) {
return; return;
} }
qDebug(
"core process state changed: %s -> %s", //
qPrintable(processStateToString(m_processState)),
qPrintable(processStateToString(state)));
m_processState = state; m_processState = state;
emit processStateChanged(state); emit processStateChanged(state);
} }

View file

@ -59,7 +59,7 @@ public:
enum class ConnectionState { Disconnected, Connecting, Connected, Listening }; enum class ConnectionState { Disconnected, Connecting, Connected, Listening };
explicit CoreProcess( explicit CoreProcess(
IAppConfig &appConfig, IServerConfig &serverConfig, const IAppConfig &appConfig, const IServerConfig &serverConfig,
const ILicense &license, const ILicense &license,
std::shared_ptr<Deps> deps = std::make_shared<Deps>()); std::shared_ptr<Deps> deps = std::make_shared<Deps>());
@ -119,8 +119,8 @@ private:
void checkOSXNotification(const QString &line); void checkOSXNotification(const QString &line);
#endif #endif
IAppConfig &m_appConfig; const IAppConfig &m_appConfig;
IServerConfig &m_serverConfig; const IServerConfig &m_serverConfig;
const ILicense &m_license; const ILicense &m_license;
std::shared_ptr<Deps> m_pDeps; std::shared_ptr<Deps> m_pDeps;
QString m_address; QString m_address;

View file

@ -21,6 +21,7 @@
#include "UpgradeDialog.h" #include "UpgradeDialog.h"
#include "gui/core/CoreProcess.h" #include "gui/core/CoreProcess.h"
#include "gui/license/license_config.h" #include "gui/license/license_config.h"
#include "gui/messages.h"
#include "gui/tls/TlsCertificate.h" #include "gui/tls/TlsCertificate.h"
#include "gui/tls/TlsUtility.h" #include "gui/tls/TlsUtility.h"
#include "gui/validators/ScreenNameValidator.h" #include "gui/validators/ScreenNameValidator.h"
@ -61,6 +62,15 @@ SettingsDialog::SettingsDialog(
m_pScreenNameError = new validators::ValidationError(this); m_pScreenNameError = new validators::ValidationError(this);
m_pLineEditScreenName->setValidator(new validators::ScreenNameValidator( m_pLineEditScreenName->setValidator(new validators::ScreenNameValidator(
m_pLineEditScreenName, m_pScreenNameError, &serverConfig.screens())); m_pLineEditScreenName, m_pScreenNameError, &serverConfig.screens()));
connect(
this, &SettingsDialog::shown, this,
[this] {
if (!m_appConfig.isActiveScopeWritable()) {
showReadOnlyMessage();
}
},
Qt::QueuedConnection);
} }
// //
@ -107,6 +117,10 @@ void SettingsDialog::on_m_pRadioSystemScope_toggled(bool checked) {
m_appConfig.setLoadFromSystemScope(checked); m_appConfig.setLoadFromSystemScope(checked);
loadFromConfig(); loadFromConfig();
updateControls(); updateControls();
if (isVisible() && !m_appConfig.isActiveScopeWritable()) {
showReadOnlyMessage();
}
} }
void SettingsDialog::on_m_pPushButtonTlsCertPath_clicked() { void SettingsDialog::on_m_pPushButtonTlsCertPath_clicked() {
@ -147,6 +161,16 @@ void SettingsDialog::on_m_pCheckBoxServiceEnabled_toggled(bool) {
// End of auto-connect slots // End of auto-connect slots
// //
void SettingsDialog::showEvent(QShowEvent *event) {
QDialog::showEvent(event);
emit shown();
}
void SettingsDialog::showReadOnlyMessage() {
const auto activeScopeFilename = m_appConfig.scopes().activeFilePath();
messages::showReadOnlySettings(this, activeScopeFilename);
}
void SettingsDialog::accept() { void SettingsDialog::accept() {
if (!m_pLineEditScreenName->hasAcceptableInput()) { if (!m_pLineEditScreenName->hasAcceptableInput()) {
QMessageBox::warning( QMessageBox::warning(
@ -228,6 +252,7 @@ void SettingsDialog::updateTlsControls() {
const auto tlsEnabled = m_tlsUtility.isAvailableAndEnabled(); const auto tlsEnabled = m_tlsUtility.isAvailableAndEnabled();
const auto writable = m_appConfig.isActiveScopeWritable(); const auto writable = m_appConfig.isActiveScopeWritable();
m_pCheckBoxEnableTls->setEnabled(writable);
m_pCheckBoxEnableTls->setChecked(writable && tlsEnabled); m_pCheckBoxEnableTls->setChecked(writable && tlsEnabled);
m_pLineEditTlsCertPath->setText(m_appConfig.tlsCertPath()); m_pLineEditTlsCertPath->setText(m_appConfig.tlsCertPath());

View file

@ -39,10 +39,14 @@ class SettingsDialog : public QDialog, public Ui::SettingsDialogBase {
Q_OBJECT Q_OBJECT
public: public:
void extracted();
SettingsDialog( SettingsDialog(
QWidget *parent, IAppConfig &appConfig, const IServerConfig &serverConfig, QWidget *parent, IAppConfig &appConfig, const IServerConfig &serverConfig,
const License &license, const CoreProcess &coreProcess); const License &license, const CoreProcess &coreProcess);
signals:
void shown();
private slots: private slots:
void on_m_pCheckBoxEnableTls_clicked(bool checked); void on_m_pCheckBoxEnableTls_clicked(bool checked);
void on_m_pCheckBoxLogToFile_stateChanged(int); void on_m_pCheckBoxLogToFile_stateChanged(int);
@ -56,6 +60,11 @@ private slots:
private: private:
void accept() override; void accept() override;
void reject() override; void reject() override;
void showEvent(QShowEvent *event) override;
bool isClientMode() const;
void updateTlsControls();
void updateTlsControlsEnabled();
void showReadOnlyMessage();
/// @brief Load all settings. /// @brief Load all settings.
void loadFromConfig(); void loadFromConfig();
@ -69,10 +78,6 @@ private:
/// @brief Enables controls when they should be. /// @brief Enables controls when they should be.
void updateControls(); void updateControls();
bool isClientMode() const;
void updateTlsControls();
void updateTlsControlsEnabled();
[[no_unique_address]] CoreTool m_coreTool; [[no_unique_address]] CoreTool m_coreTool;
validators::ValidationError *m_pScreenNameError; validators::ValidationError *m_pScreenNameError;

View file

@ -23,9 +23,8 @@
class QDataStreamProxy { class QDataStreamProxy {
public: public:
explicit QDataStreamProxy() = default; explicit QDataStreamProxy() = default;
explicit QDataStreamProxy(QTcpSocket *socket) { explicit QDataStreamProxy(QTcpSocket *socket)
m_Stream = std::make_unique<QDataStream>(socket); : m_Stream(std::make_unique<QDataStream>(socket)) {}
}
virtual ~QDataStreamProxy() = default; virtual ~QDataStreamProxy() = default;
virtual qint64 writeRawData(const char *data, int len) { virtual qint64 writeRawData(const char *data, int len) {

View file

@ -79,14 +79,17 @@ void QIpcClient::connectToHost() {
} }
void QIpcClient::disconnectFromHost() { void QIpcClient::disconnectFromHost() {
m_isConnecting = false;
qInfo("disconnected from background service");
m_pReader->stop(); m_pReader->stop();
m_pSocket->flush();
m_pSocket->close(); m_pSocket->close();
m_isConnecting = false;
m_isConnected = false; m_isConnected = false;
qInfo("disconnected from background service");
} }
void QIpcClient::onSocketError(QAbstractSocket::SocketError socketError) const { void QIpcClient::onSocketError(QAbstractSocket::SocketError socketError) {
QString text; QString text;
switch (socketError) { switch (socketError) {
case 0: case 0:
@ -101,6 +104,7 @@ void QIpcClient::onSocketError(QAbstractSocket::SocketError socketError) const {
} }
qWarning("ipc connection error, %s", qUtf8Printable(text)); qWarning("ipc connection error, %s", qUtf8Printable(text));
m_isConnected = false;
QTimer::singleShot(kRetryInterval, this, &QIpcClient::onRetryConnect); QTimer::singleShot(kRetryInterval, this, &QIpcClient::onRetryConnect);
} }
@ -154,6 +158,12 @@ void QIpcClient::sendCommand(
void QIpcClient::onIpcReaderHelloBack() { void QIpcClient::onIpcReaderHelloBack() {
qDebug("ipc hello back received"); qDebug("ipc hello back received");
if (m_isConnected) {
qWarning("ipc already connected, ignoring hello back");
return;
}
m_isConnected = true; m_isConnected = true;
serviceReady(); serviceReady();
} }

View file

@ -48,7 +48,7 @@ private slots:
void onRetryConnect(); void onRetryConnect();
void onSocketConnected() const; void onSocketConnected() const;
void onIpcReaderHelloBack(); void onIpcReaderHelloBack();
void onSocketError(QAbstractSocket::SocketError error) const; void onSocketError(QAbstractSocket::SocketError error);
void onIpcReaderRead(const QString &text); void onIpcReaderRead(const QString &text);
private: private:

View file

@ -149,7 +149,16 @@ void showCloseReminder(QWidget *parent) {
QMessageBox::information(parent, "Notification area icon", message); QMessageBox::information(parent, "Notification area icon", message);
} }
void showFirstRunMessage( void showFirstServerStartMessage(QWidget *parent) {
QMessageBox::information(
parent, "Server is running",
"<p>Great, the server is now running.</p>"
"<p>Now you can connect your other computers to this server. "
"You should see a prompt here on the server when a new client tries to "
"connect.</p>");
}
void showFirstConnectedMessage(
QWidget *parent, bool closeToTray, bool enableService, bool isServer) { QWidget *parent, bool closeToTray, bool enableService, bool isServer) {
auto message = QString("<p>Synergy is now connected!</p>"); auto message = QString("<p>Synergy is now connected!</p>");
@ -265,4 +274,13 @@ bool showClearSettings(QWidget *parent) {
return message.clickedButton() == clear; return message.clickedButton() == clear;
} }
void showReadOnlySettings(QWidget *parent, const QString &systemSettingsPath) {
QString nativePath = QDir::toNativeSeparators(systemSettingsPath);
QMessageBox::information(
parent, "Read-only settings",
QString("<p>Settings are read-only because you only have read access "
"to the file:</p><p>%1</p>")
.arg(nativePath));
}
} // namespace synergy::gui::messages } // namespace synergy::gui::messages

View file

@ -33,7 +33,9 @@ void messageHandler(
void raiseCriticalDialog(); void raiseCriticalDialog();
void showFirstRunMessage( void showFirstServerStartMessage(QWidget *parent);
void showFirstConnectedMessage(
QWidget *parent, bool closeToTray, bool enableService, bool isServer); QWidget *parent, bool closeToTray, bool enableService, bool isServer);
void showCloseReminder(QWidget *parent); void showCloseReminder(QWidget *parent);
@ -48,4 +50,6 @@ showNewClientPrompt(QWidget *parent, const QString &clientName);
bool showClearSettings(QWidget *parent); bool showClearSettings(QWidget *parent);
void showReadOnlySettings(QWidget *parent, const QString &systemSettingsPath);
} // namespace synergy::gui::messages } // namespace synergy::gui::messages

View file

@ -29,26 +29,29 @@
namespace synergy::gui::proxy { namespace synergy::gui::proxy {
const auto kLegacyOrgDomain = "http-symless-com"; const auto kLegacyOrgDomain = "http-symless-com";
const auto kLegacySystemConfigFilename = "SystemConfig.ini";
const auto kSystemConfigFilename = "SystemConfig.ini";
#if defined(Q_OS_UNIX) #if defined(Q_OS_UNIX)
const auto kUnixSystemConfigPath = "/usr/local/etc/symless/"; const auto kUnixSystemConfigPath = "/usr/local/etc/";
#endif #endif
// //
// Free functions // Free functions
// //
QString getSystemSettingPath() { /**
const QString settingFilename(kSystemConfigFilename); * @brief The base dir for the system settings file.
*
* Important: Qt will append the org name as a dir, and the app name as the
* settings filename, i.e.: `{base-dir}/Synergy/Synergy.ini`
*/
QString getSystemSettingsBaseDir() {
#if defined(Q_OS_WIN) #if defined(Q_OS_WIN)
return QCoreApplication::applicationDirPath() + QDir::separator(); return QCoreApplication::applicationDirPath();
#elif defined(Q_OS_MAC) #elif defined(Q_OS_UNIX)
// it would be nice to use /Library dir, but qt has no elevate system. // Qt already adds application and filename to the end of the path.
return kUnixSystemConfigPath + settingFilename; // On macOS, it would be nice to use /Library dir, but qt has no elevate
#elif defined(Q_OS_LINUX) // system.
// qt already adds application and filename to the end of the path on linux.
return kUnixSystemConfigPath; return kUnixSystemConfigPath;
#else #else
#error "unsupported platform" #error "unsupported platform"
@ -62,7 +65,8 @@ void migrateLegacySystemSettings(QSettings &settings) {
} }
QSettings::setPath( QSettings::setPath(
QSettings::IniFormat, QSettings::SystemScope, kSystemConfigFilename); QSettings::IniFormat, QSettings::SystemScope,
kLegacySystemConfigFilename);
QSettings oldSystemSettings( QSettings oldSystemSettings(
QSettings::IniFormat, QSettings::SystemScope, QSettings::IniFormat, QSettings::SystemScope,
QCoreApplication::organizationName(), QCoreApplication::organizationName(),
@ -75,7 +79,7 @@ void migrateLegacySystemSettings(QSettings &settings) {
} }
QSettings::setPath( QSettings::setPath(
QSettings::IniFormat, QSettings::SystemScope, getSystemSettingPath()); QSettings::IniFormat, QSettings::SystemScope, getSystemSettingsBaseDir());
} }
void migrateLegacyUserSettings(QSettings &newSettings) { void migrateLegacyUserSettings(QSettings &newSettings) {
@ -151,7 +155,7 @@ void QSettingsProxy::loadSystem() {
QSettings::setPath( QSettings::setPath(
QSettings::Format::IniFormat, QSettings::Scope::SystemScope, QSettings::Format::IniFormat, QSettings::Scope::SystemScope,
getSystemSettingPath()); getSystemSettingsBaseDir());
m_pSettings = std::make_unique<QSettings>( m_pSettings = std::make_unique<QSettings>(
QSettings::Format::IniFormat, QSettings::Scope::SystemScope, orgName, QSettings::Format::IniFormat, QSettings::Scope::SystemScope, orgName,

View file

@ -21,7 +21,7 @@
namespace synergy::gui::proxy { namespace synergy::gui::proxy {
QString getSystemSettingPath(); QString getSystemSettingBaseDir();
class QSettingsProxy { class QSettingsProxy {
public: public:

View file

@ -57,9 +57,27 @@ Screen::~Screen() {
} }
assert(!m_enabled); assert(!m_enabled);
// TODO: why assert this? it appears to be false when an elevated dialog // Originally there was an assert here added before 2009 (history not in
// appears on windows and the process is killed. // tact). This condition seems to occur on a Windows client when the process
assert(m_entered == m_isPrimary); // is shut down to make way for a new elevated process (e.g. at login screen).
// The reason why this assert was originally added is unclear, and was causing
// pain when using debug builds; you lose control of the client when it's at
// the login screen. Therefore it has been converted to a warning so that we
// can still see when it happens but it won't cause the process to pause. This
// also gives us the added benefit of seeing when it happens in production.
// Perhaps it indicates that the cursor is still being controlled on the
// client while it's shutting down? i.e. the screen is entered and is not the
// server, or the screen is not entered and is the server.
if (m_entered == m_isPrimary) {
LOG(
(CLOG_DEBUG "current screen: entered=%s, primary=%s", //
m_entered ? "yes" : "no", m_isPrimary ? "yes" : "no"));
if (m_isPrimary) {
LOG((CLOG_WARN "current primary screen is not entered on shutdown"));
} else {
LOG((CLOG_WARN "current secondary screen is entered on shutdown"));
}
}
delete m_screen; delete m_screen;
LOG((CLOG_DEBUG "closed display")); LOG((CLOG_DEBUG "closed display"));

View file

@ -39,9 +39,10 @@ public:
} }
// //
// Setters // Getters
// //
MOCK_METHOD(synergy::gui::IConfigScopes &, scopes, (), (const, override));
MOCK_METHOD(QString, tlsCertPath, (), (const, override)); MOCK_METHOD(QString, tlsCertPath, (), (const, override));
MOCK_METHOD(int, tlsKeyLength, (), (const, override)); MOCK_METHOD(int, tlsKeyLength, (), (const, override));
MOCK_METHOD(bool, tlsEnabled, (), (const, override)); MOCK_METHOD(bool, tlsEnabled, (), (const, override));
@ -73,7 +74,7 @@ public:
MOCK_METHOD(bool, clientGroupChecked, (), (const, override)); MOCK_METHOD(bool, clientGroupChecked, (), (const, override));
// //
// Getters // Setters
// //
MOCK_METHOD( MOCK_METHOD(

View file

@ -49,6 +49,7 @@ public:
MOCK_METHOD(const QSettingsProxy &, activeSettings, (), (const, override)); MOCK_METHOD(const QSettingsProxy &, activeSettings, (), (const, override));
MOCK_METHOD(QSettingsProxy &, activeSettings, (), (override)); MOCK_METHOD(QSettingsProxy &, activeSettings, (), (override));
MOCK_METHOD(void, save, (bool), (override)); MOCK_METHOD(void, save, (bool), (override));
MOCK_METHOD(QString, activeFilePath, (), (const, override));
}; };
struct DepsMock : public AppConfig::Deps { struct DepsMock : public AppConfig::Deps {