Refactored core process management and IPC client (#7425)
* Also ignore `command_pre` for Fedora itself * Improve warning message * Fixed layout issues on main window * Restore fusion theme for Windows dark mode * Further correct main window layout * Set CWD for launch on Windows to same dir as .env * Use signals for tray icon instead of callback * Reduce complexity for setting tray icon * Further reduce tray icon complexity * Reduce tray retry time * Fixed tray not showing on macOS * Refactor function names * Move tray icon to lib * Decouple server connection class * Move server connection to lib * Move client config to lib * Remove redundant forward declarations * Fixed some namespaces in the new lib * Move core process code to new class * Improve member names on new process class * Remove copy/pasta code * Move OSX helpers to lib * Add .mm to lib config * Fixed copyright * Improve reliability of log line handling * Fixed TLS certificate generate bugs * Remove client list * Refactor core process handling to fix various problems * Fixed process/connection status bugs * Fixed function signature issue on macOS * Fixed override warnings * Fixed string format warning * Save `wasStarted` state and use that instead * Use only filename in dialog * Use lambda for simple slot * Scroll to bottom if at bottome * Set value based on position before text added and set horizontal scroll too * Add 1px tollerance for Linux * Simplify start/stop mutex * Always stop service on restart * Increase scroll bottom threshold to 2 * Log warning instead of critical * Fixed long-standing dir CD-up hack * Remove include * Remove include * Fixed rogue dumbisense includes * Account for optional distro_like * Add QAction include to solve incomplete type * Remove rogue #pragma * Static cast log value * Solve event queue delete warning * Fixed integ test on Windows 10 * Reduce enum verbosity * Use static instance instead of global * Make function const * Use unique_ptr instead of new and delete, and made some functions const * Fixed smart pointer use * Fixed variable shadowing * Fixed wrong use of using * Fixed missing namespace using * Simplify TLS error handling * Improve UX around certificate errors and success * Decouple app config from core process through interface * First iteration of core process test * Mark dtor as override * Rename attach launch entry * Add TODO * Create proxy for process * Move IPC client to deps * Fixed warnings * Reorganize new GUI lib dir structure * Update includes to reflect new paths * Reorg GUI tests * Abstract IPC client * Refactor about screen * Fixed .ui warnings * Remove redundant include * Fixed typo * Fixed typos and add spelling * Fixed misleading function name * Improve comment * Improve code coverage for core process * Remove noisy log line * Make global const * Use default dtor * Use vector instead of new * Make ctor explicit * Make ctor explicit (2) * Use enum class * Fixed bad enum * Stub out core tool * Stub out dir operation * Extract byte functions * Add missing return path * Simplify byte/int functions * Fix truncation * Use ctor member assignment * Fixed segfault in process proxy * Fixed print warning * Make function const * Cleanup header * Fixed missing name * Update ChangeLog * Make more functions const
This commit is contained in:
parent
0849010e38
commit
7984535d38
97 changed files with 3024 additions and 1734 deletions
|
|
@ -12,6 +12,12 @@
|
|||
# Version checker URL to use (useful for testing)
|
||||
# SYNERGY_VERSION_URL="http://localhost:8787?version=v1"
|
||||
|
||||
# Enable debug logging in the GUI (on by default in debug mode)
|
||||
# SYNERGY_GUI_DEBUG=true
|
||||
|
||||
# Enable verbose logging in the GUI (always off by default)
|
||||
# SYNERGY_GUI_VERBOSE=true
|
||||
|
||||
#
|
||||
# Build
|
||||
#
|
||||
|
|
|
|||
4
.github/workflows/ci.yml
vendored
4
.github/workflows/ci.yml
vendored
|
|
@ -26,8 +26,8 @@ env:
|
|||
SYNERGY_VERSION: ${{ github.event.inputs.version || github.event.release.tag_name }}
|
||||
SYNERGY_PRODUCT_NAME: ${{ vars.SYNERGY_PRODUCT_NAME }}
|
||||
SYNERGY_PACKAGE_PREFIX: ${{ vars.SYNERGY_PACKAGE_PREFIX }}
|
||||
SYNERGY_ENABLE_LICENSING: ${{ vars.SYNERGY_ENABLE_LICENSING }}
|
||||
SYNERGY_SHOW_DEV_THANKS: ${{ vars.SYNERGY_SHOW_DEV_THANKS }}
|
||||
SYNERGY_LICENSED_PRODUCT: ${{ vars.SYNERGY_LICENSED_PRODUCT }}
|
||||
SYNERGY_ENABLE_ACTIVATION: ${{ vars.SYNERGY_ENABLE_ACTIVATION }}
|
||||
PACKAGE_BUILD: ${{ !github.event.pull_request.draft }}
|
||||
PACKAGE_UPLOAD: ${{ !github.event.pull_request.draft && github.event_name != 'schedule' }}
|
||||
UPLOAD_TO_GITHUB: ${{ github.event_name == 'pull_request' && !github.event.pull_request.draft }}
|
||||
|
|
|
|||
4
.vscode/launch.json
vendored
4
.vscode/launch.json
vendored
|
|
@ -11,7 +11,7 @@
|
|||
{
|
||||
"name": "gui windows",
|
||||
"type": "cppvsdbg",
|
||||
"cwd": "${workspaceRoot}/build/bin",
|
||||
"cwd": "${workspaceRoot}",
|
||||
"request": "launch",
|
||||
"program": "${workspaceFolder}/build/bin/synergy",
|
||||
"internalConsoleOptions": "openOnSessionStart",
|
||||
|
|
@ -60,7 +60,7 @@
|
|||
"preLaunchTask": "build"
|
||||
},
|
||||
{
|
||||
"name": "daemon windows attach",
|
||||
"name": "windows attach",
|
||||
"type": "cppvsdbg",
|
||||
"request": "attach",
|
||||
"processId": "${command:pickProcess}"
|
||||
|
|
|
|||
|
|
@ -68,6 +68,7 @@ Enhancements:
|
|||
- #7422 Handle empty value for computer name setting
|
||||
- #7423 Refactor local/global save/load and show dev thanks message
|
||||
- #7424 Add Alma Linux and Rocky Linux runners
|
||||
- #7425 Refactored core process management and IPC client
|
||||
|
||||
# 1.14.6
|
||||
|
||||
|
|
|
|||
|
|
@ -45,14 +45,20 @@ macro(configure_definitions)
|
|||
add_definitions(-DGIT_SHA_SHORT="${GIT_SHA_SHORT}")
|
||||
endif()
|
||||
|
||||
if(ENABLE_LICENSING)
|
||||
message(STATUS "Licensing enabled")
|
||||
add_definitions(-DSYNERGY_ENABLE_LICENSING=1)
|
||||
else()
|
||||
set(PRODUCT_NAME "Synergy 1 Community Edition")
|
||||
if(NOT "$ENV{SYNERGY_PRODUCT_NAME}" STREQUAL "")
|
||||
set(PRODUCT_NAME $ENV{SYNERGY_PRODUCT_NAME})
|
||||
endif()
|
||||
|
||||
if(LICENSED_PRODUCT)
|
||||
message(STATUS "Licensed product")
|
||||
add_definitions(-DSYNERGY_LICENSED_PRODUCT=1)
|
||||
|
||||
if(ENABLE_ACTIVATION)
|
||||
message(STATUS "Activation enabled")
|
||||
add_definitions(-DSYNERGY_ENABLE_ACTIVATION=1)
|
||||
endif()
|
||||
else()
|
||||
set(PRODUCT_NAME "Synergy 1 Community Edition")
|
||||
endif()
|
||||
|
||||
if(PRODUCT_NAME)
|
||||
|
|
@ -110,10 +116,13 @@ macro(configure_options)
|
|||
# coverage is off by default because it's GCC only and a developer preference.
|
||||
set(DEFAULT_ENABLE_COVERAGE OFF)
|
||||
|
||||
# licensing is off by default to make life easier for contributors.
|
||||
set(DEFAULT_ENABLE_LICENSING OFF)
|
||||
# licensed product is off by default to show links to github, etc.
|
||||
set(DEFAULT_LICENSED_PRODUCT OFF)
|
||||
|
||||
# by default, show the dev thanks message.
|
||||
# activation is off by default to make life easier for contributors.
|
||||
set(DEFAULT_ENABLE_ACTIVATION OFF)
|
||||
|
||||
# by default, show the dev thanks message, guides contributions, etc.
|
||||
set(DEFAULT_SHOW_DEV_THANKS ON)
|
||||
|
||||
if("$ENV{SYNERGY_BUILD_MINIMAL}" STREQUAL "true")
|
||||
|
|
@ -129,8 +138,12 @@ macro(configure_options)
|
|||
set(DEFAULT_BUILD_UNIFIED ON)
|
||||
endif()
|
||||
|
||||
if("$ENV{SYNERGY_ENABLE_LICENSING}" STREQUAL "true")
|
||||
set(DEFAULT_ENABLE_LICENSING ON)
|
||||
if("$ENV{SYNERGY_ENABLE_ACTIVATION}" STREQUAL "true")
|
||||
set(DEFAULT_ENABLE_ACTIVATION ON)
|
||||
endif()
|
||||
|
||||
if("$ENV{SYNERGY_LICENSED_PRODUCT}" STREQUAL "true")
|
||||
set(DEFAULT_LICENSED_PRODUCT ON)
|
||||
endif()
|
||||
|
||||
if("$ENV{SYNERGY_ENABLE_COVERAGE}" STREQUAL "true")
|
||||
|
|
@ -145,7 +158,8 @@ macro(configure_options)
|
|||
option(BUILD_INSTALLER "Build installer" ${DEFAULT_BUILD_INSTALLER})
|
||||
option(BUILD_TESTS "Build tests" ${DEFAULT_BUILD_TESTS})
|
||||
option(BUILD_UNIFIED "Build unified binary" ${DEFAULT_BUILD_UNIFIED})
|
||||
option(ENABLE_LICENSING "Enable licensing" ${DEFAULT_ENABLE_LICENSING})
|
||||
option(ENABLE_ACTIVATION "Enable activation" ${DEFAULT_ENABLE_ACTIVATION})
|
||||
option(LICENSED_PRODUCT "Show licensing info" ${DEFAULT_LICENSED_PRODUCT})
|
||||
option(ENABLE_COVERAGE "Enable test coverage" ${DEFAULT_ENABLE_COVERAGE})
|
||||
option(SHOW_DEV_THANKS "Show dev thanks message" ${DEFAULT_SHOW_DEV_THANKS})
|
||||
|
||||
|
|
|
|||
25
cspell.json
25
cspell.json
|
|
@ -4,23 +4,46 @@
|
|||
"dictionaryDefinitions": [],
|
||||
"dictionaries": [],
|
||||
"words": [
|
||||
"Schoeneman",
|
||||
"aqtinstall",
|
||||
"Axelson",
|
||||
"Breen",
|
||||
"codesign",
|
||||
"codesigning",
|
||||
"contribs",
|
||||
"Daun",
|
||||
"distros",
|
||||
"dmgbuild",
|
||||
"dotenv",
|
||||
"Evenson",
|
||||
"Feder",
|
||||
"gdrive",
|
||||
"Hadzhylov",
|
||||
"Hetu",
|
||||
"keychain",
|
||||
"Keychains",
|
||||
"Kutytska",
|
||||
"Lanz",
|
||||
"LLDB",
|
||||
"Lysytsia",
|
||||
"macdeployqt",
|
||||
"msvc",
|
||||
"noquote",
|
||||
"notarytool",
|
||||
"Oleksandr",
|
||||
"Olena",
|
||||
"outputdir",
|
||||
"Petroules",
|
||||
"Poschta",
|
||||
"Priddy",
|
||||
"runas",
|
||||
"Sbârnea",
|
||||
"Schoeneman",
|
||||
"Serhii",
|
||||
"Sorin",
|
||||
"synergyc",
|
||||
"synergyd",
|
||||
"synergys",
|
||||
"Volker",
|
||||
"winget"
|
||||
],
|
||||
"ignoreWords": [],
|
||||
|
|
|
|||
|
|
@ -133,9 +133,12 @@ class Dependencies:
|
|||
print("Running dependencies prerequisites command")
|
||||
|
||||
check = True
|
||||
if distro_like and "fedora" in distro_like:
|
||||
print("Fedora-like detected, ignoring return code")
|
||||
# On Fedora, dnf update returns code 100 when updates are available.
|
||||
if distro == "fedora" or (distro_like and "fedora" in distro_like):
|
||||
print(
|
||||
"Fedora-like detected, "
|
||||
"ignoring return code on dependencies prerequisites command"
|
||||
)
|
||||
# On Fedora-like, dnf update returns code 100 when updates are available.
|
||||
check = False
|
||||
|
||||
linux.run_command(command_pre, check)
|
||||
|
|
|
|||
|
|
@ -29,11 +29,6 @@ file(
|
|||
src/widgets/*)
|
||||
file(GLOB GUI_UI_FILES src/*.ui)
|
||||
|
||||
if(${CMAKE_SYSTEM_NAME} MATCHES "Darwin")
|
||||
file(GLOB GUI_MAC_SOURCE_FILES src/*.mm)
|
||||
list(APPEND GUI_SOURCE_FILES ${GUI_MAC_SOURCE_FILES})
|
||||
endif()
|
||||
|
||||
if(WIN32)
|
||||
set(GUI_RC_FILES res/win/Synergy.rc ${CMAKE_BINARY_DIR}/src/version.rc)
|
||||
endif()
|
||||
|
|
|
|||
|
|
@ -21,7 +21,7 @@
|
|||
#include <QDateTime>
|
||||
|
||||
#if defined(Q_OS_MAC)
|
||||
#include "OSXHelpers.h"
|
||||
#include "gui/OSXHelpers.h"
|
||||
#endif
|
||||
|
||||
#include "gui/version.h"
|
||||
|
|
@ -29,8 +29,11 @@
|
|||
AboutDialog::AboutDialog(MainWindow *parent)
|
||||
: QDialog(parent, Qt::WindowTitleHint | Qt::WindowSystemMenuHint),
|
||||
Ui::AboutDialogBase() {
|
||||
|
||||
setupUi(this);
|
||||
|
||||
this->setFixedSize(this->size());
|
||||
|
||||
m_pLabelSynergyVersion->setText(synergy::gui::version());
|
||||
|
||||
QString buildDateString = QString::fromLocal8Bit(__DATE__).simplified();
|
||||
|
|
@ -59,21 +62,47 @@ void AboutDialog::updateLogo() const {
|
|||
}
|
||||
|
||||
QString AboutDialog::getImportantDevelopers() const {
|
||||
return QString(
|
||||
// The ultimate creator
|
||||
"Chris Schoeneman, "
|
||||
QStringList awesomePeople;
|
||||
|
||||
// Precursor developers
|
||||
"Richard Lee, Adam Feder, "
|
||||
// Chris is the ultimate creator, and the one who started it all in 2001.
|
||||
awesomePeople << "Chris Schoeneman";
|
||||
|
||||
// Contributors
|
||||
"Nick Bolton, Volker Lanz, Ryan Breen, Guido Poschta, "
|
||||
"Bertrand Landry Hetu, Tom Chadwick, Brent Priddy, Jason Axelson, "
|
||||
"Jake Petroules, Sorin Sbârnea, "
|
||||
// Richard and Adam developed CosmoSynergy, the 90's predecessor project.
|
||||
awesomePeople << "Richard Lee" << "Adam Feder";
|
||||
|
||||
// Symless employees
|
||||
"Kyle Bloom, Daun Chung, Serhii Hadzhylov, "
|
||||
"Oleksandr Lysytsia, Olena Kutytska.");
|
||||
// Nick continued the legacy in 2009 started by Chris.
|
||||
awesomePeople << "Nick Bolton";
|
||||
|
||||
// Volker wrote the first version of the GUI (QSynergy) in 2008.
|
||||
awesomePeople << "Volker Lanz";
|
||||
|
||||
// Re-ignited the project in 2008 and rebuilt the community.
|
||||
awesomePeople << "Sorin Sbârnea";
|
||||
|
||||
// Contributors of bug fixes in the early days.
|
||||
awesomePeople << "Ryan Breen"
|
||||
<< "Guido Poschta"
|
||||
<< "Bertrand Landry Hetu"
|
||||
<< "Tom Chadwick"
|
||||
<< "Brent Priddy"
|
||||
<< "Jason Axelson"
|
||||
<< "Jake Petroules";
|
||||
|
||||
// Symless employees (in order of joining).
|
||||
awesomePeople << "Kyle Bloom"
|
||||
<< "Daun Chung"
|
||||
<< "Serhii Hadzhylov"
|
||||
<< "Oleksandr Lysytsia"
|
||||
<< "Olena Kutytska"
|
||||
<< "Owen Phillips"
|
||||
<< "Daniel Evenson";
|
||||
|
||||
for (auto &person : awesomePeople) {
|
||||
// prevent names from breaking on the space when wrapped
|
||||
person = person.replace(" ", QString::fromUtf8(" "));
|
||||
}
|
||||
|
||||
return awesomePeople.join(", ") + ".";
|
||||
}
|
||||
|
||||
QString AboutDialog::getCopyright() const {
|
||||
|
|
|
|||
|
|
@ -12,28 +12,10 @@
|
|||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>450</width>
|
||||
<height>495</height>
|
||||
<width>600</width>
|
||||
<height>400</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>450</width>
|
||||
<height>495</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>450</width>
|
||||
<height>495</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>About Synergy</string>
|
||||
</property>
|
||||
|
|
@ -90,7 +72,6 @@
|
|||
<widget class="QLabel" name="m_pTitleLabel">
|
||||
<property name="font">
|
||||
<font>
|
||||
<weight>75</weight>
|
||||
<bold>true</bold>
|
||||
</font>
|
||||
</property>
|
||||
|
|
@ -161,6 +142,9 @@
|
|||
<property name="text">
|
||||
<string>Developers</string>
|
||||
</property>
|
||||
<property name="textFormat">
|
||||
<enum>Qt::RichText</enum>
|
||||
</property>
|
||||
<property name="wordWrap">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
|
|
|
|||
|
|
@ -19,10 +19,10 @@
|
|||
|
||||
#include "CancelActivationDialog.h"
|
||||
#include "MainWindow.h"
|
||||
#include "gui/AppConfig.h"
|
||||
#include "gui/LicenseHandler.h"
|
||||
#include "gui/config/AppConfig.h"
|
||||
#include "gui/constants.h"
|
||||
#include "gui/license_notices.h"
|
||||
#include "gui/license/LicenseHandler.h"
|
||||
#include "gui/license/license_notices.h"
|
||||
#include "gui/styles.h"
|
||||
#include "license/ProductEdition.h"
|
||||
#include "license/parse_serial_key.h"
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include "gui/LicenseHandler.h"
|
||||
#include "gui/license/LicenseHandler.h"
|
||||
|
||||
#include <QDialog>
|
||||
|
||||
|
|
@ -42,8 +42,8 @@ public:
|
|||
};
|
||||
|
||||
public slots:
|
||||
void reject();
|
||||
void accept();
|
||||
void reject() override;
|
||||
void accept() override;
|
||||
|
||||
protected:
|
||||
void refreshSerialKey();
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -24,21 +24,19 @@
|
|||
#include <QSettings>
|
||||
#include <QSystemTrayIcon>
|
||||
#include <QThread>
|
||||
#include <memory>
|
||||
|
||||
#include "ui_MainWindowBase.h"
|
||||
|
||||
#include "ActivationDialog.h"
|
||||
#include "ClientConnection.h"
|
||||
#include "ServerConfig.h"
|
||||
#include "ServerConnection.h"
|
||||
#include "TrayIcon.h"
|
||||
#include "global/Ipc.h"
|
||||
#include "gui/AppConfig.h"
|
||||
#include "gui/ConfigScopes.h"
|
||||
#include "gui/QIpcClient.h"
|
||||
#include "gui/TlsUtility.h"
|
||||
#include "gui/TrayIcon.h"
|
||||
#include "gui/VersionChecker.h"
|
||||
#include "gui/config/AppConfig.h"
|
||||
#include "gui/config/ConfigScopes.h"
|
||||
#include "gui/core/ClientConnection.h"
|
||||
#include "gui/core/CoreProcess.h"
|
||||
#include "gui/core/ServerConnection.h"
|
||||
#include "gui/tls/TlsUtility.h"
|
||||
#include "ui_MainWindowBase.h"
|
||||
|
||||
class QAction;
|
||||
class QMenu;
|
||||
|
|
@ -53,35 +51,22 @@ class QRadioButton;
|
|||
class QMessageBox;
|
||||
class QAbstractButton;
|
||||
|
||||
class LogDialog;
|
||||
class QSynergyApplication;
|
||||
class SetupWizard;
|
||||
class DataDownloader;
|
||||
class CommandProcess;
|
||||
class TlsCertificate;
|
||||
|
||||
class MainWindow : public QMainWindow, public Ui::MainWindowBase {
|
||||
using CoreMode = synergy::gui::CoreProcess::Mode;
|
||||
using CoreProcess = synergy::gui::CoreProcess;
|
||||
|
||||
Q_OBJECT
|
||||
|
||||
friend class QSynergyApplication;
|
||||
friend class SetupWizard;
|
||||
friend class ActivationDialog;
|
||||
friend class SettingsDialog;
|
||||
friend class ServerConnection;
|
||||
friend class ClientConnection;
|
||||
|
||||
public:
|
||||
enum class CoreState {
|
||||
Disconnected,
|
||||
Connecting,
|
||||
Connected,
|
||||
Listening,
|
||||
PendingRetry
|
||||
};
|
||||
|
||||
enum class CoreMode { None, Client, Server };
|
||||
enum class LogLevel { Error, Info };
|
||||
enum class RuningState { Started, Stopped };
|
||||
|
||||
public:
|
||||
explicit MainWindow(
|
||||
|
|
@ -89,17 +74,12 @@ public:
|
|||
~MainWindow() override;
|
||||
|
||||
void setVisible(bool visible) override;
|
||||
CoreMode coreMode() const;
|
||||
QString coreModeString() const;
|
||||
CoreMode coreMode() const { return m_CoreProcess.mode(); }
|
||||
QString address() const;
|
||||
QString appPath(const QString &name) const;
|
||||
void open();
|
||||
ServerConfig &serverConfig() { return m_ServerConfig; }
|
||||
void autoAddScreen(const QString name);
|
||||
int showActivationDialog();
|
||||
void appendLogInfo(const QString &text);
|
||||
void appendLogDebug(const QString &text);
|
||||
void appendLogError(const QString &text);
|
||||
|
||||
signals:
|
||||
void created();
|
||||
|
|
@ -115,19 +95,19 @@ private slots:
|
|||
void onAppConfigTlsChanged();
|
||||
void onAppConfigScreenNameChanged();
|
||||
void onAppConfigInvertConnection();
|
||||
void onCoreProcessStarting();
|
||||
void onCoreProcessError(CoreProcess::Error error);
|
||||
void onCoreConnectionStateChanged(CoreProcess::ConnectionState state);
|
||||
void onCoreProcessStateChanged(CoreProcess::ProcessState state);
|
||||
void onCoreProcessSecureSocket(bool enabled);
|
||||
void onLicenseHandlerSerialKeyChanged(const QString &serialKey);
|
||||
void onLicenseHandlerInvalidLicense();
|
||||
void onIpcClientReadLogLine(const QString &text);
|
||||
void onIpcClientErrorMessage(const QString &text);
|
||||
void onIpcClientInfoMessage(const QString &text);
|
||||
void onCoreProcessFinished(int exitCode, QProcess::ExitStatus);
|
||||
void onVersionCheckerUpdateFound(const QString &version);
|
||||
void onTrayIconCreate(QSystemTrayIcon::ActivationReason reason);
|
||||
void onTrayIconActivated(QSystemTrayIcon::ActivationReason reason);
|
||||
void onActionStartCoreTriggered();
|
||||
void onActionStopCoreTriggered();
|
||||
void onCoreProcessReadyReadStandardOutput();
|
||||
void onCoreProcessReadyReadStandardError();
|
||||
void onWindowSaveTimerTimeout();
|
||||
void onServerConnectionConfigureClient(const QString &clientName);
|
||||
|
||||
// autoconnect slots
|
||||
void on_m_pButtonApply_clicked();
|
||||
|
|
@ -145,6 +125,8 @@ private slots:
|
|||
void on_m_pActivate_triggered();
|
||||
void on_m_pLineEditHostname_returnPressed();
|
||||
void on_m_pLineEditClientIp_returnPressed();
|
||||
void on_m_pLineEditHostname_textChanged(const QString &text);
|
||||
void on_m_pLineEditClientIp_textChanged(const QString &text);
|
||||
|
||||
private:
|
||||
AppConfig &appConfig() { return m_AppConfig; }
|
||||
|
|
@ -154,44 +136,32 @@ private:
|
|||
void createTrayIcon();
|
||||
void applyConfig();
|
||||
void applyCloseToTray() const;
|
||||
void setIcon(CoreState state) const;
|
||||
void setCoreState(CoreState state);
|
||||
void setIcon(CoreProcess::ConnectionState state);
|
||||
bool checkForApp(int which, QString &app);
|
||||
bool clientArgs(QStringList &args, QString &app);
|
||||
bool serverArgs(QStringList &args, QString &app);
|
||||
void setStatus(const QString &status);
|
||||
void sendIpcMessage(IpcMessageType type, const char *buffer, bool showErrors);
|
||||
void updateFromLogLine(const QString &line);
|
||||
QString getIPAddresses() const;
|
||||
void stopService();
|
||||
void stopDesktop();
|
||||
void enableServer(bool enable);
|
||||
void enableClient(bool enable);
|
||||
QString getProfileRootForArg() const;
|
||||
void checkConnected(const QString &line);
|
||||
void checkFingerprint(const QString &line);
|
||||
bool checkSecureSocket(const QString &line);
|
||||
void checkLicense(const QString &line);
|
||||
QString getTimeStamp() const;
|
||||
void restartCore();
|
||||
void showEvent(QShowEvent *) override;
|
||||
void closeEvent(QCloseEvent *event) override;
|
||||
void secureSocket(bool secureSocket);
|
||||
void windowStateChanged();
|
||||
void connectSlots() const;
|
||||
void connectSlots();
|
||||
void updateWindowTitle();
|
||||
void processCoreLogLine(const QString &line);
|
||||
void startCore();
|
||||
void onCoreProcessRetryStart();
|
||||
void handleLogLine(const QString &line);
|
||||
void updateLocalFingerprint();
|
||||
void updateScreenName();
|
||||
void saveSettings();
|
||||
QString configFilename();
|
||||
bool isCoreActive() const;
|
||||
void showConfigureServer(const QString &message);
|
||||
void showConfigureServer() { showConfigureServer(""); }
|
||||
void showLicenseNotice();
|
||||
void stopCore();
|
||||
void restoreWindow();
|
||||
void saveWindow();
|
||||
void setupControls();
|
||||
|
|
@ -200,37 +170,28 @@ private:
|
|||
void showFirstRunMessage();
|
||||
void showDevThanksMessage();
|
||||
QString productName() const;
|
||||
|
||||
#ifdef Q_OS_MAC
|
||||
void checkOSXNotification(const QString &line);
|
||||
#endif
|
||||
void updateStatus();
|
||||
|
||||
VersionChecker m_VersionChecker;
|
||||
QIpcClient m_IpcClient;
|
||||
TrayIcon m_TrayIcon;
|
||||
QMutex m_StopDesktopMutex;
|
||||
synergy::gui::TrayIcon m_TrayIcon;
|
||||
bool m_ActivationDialogRunning = false;
|
||||
QStringList m_PendingClientNames;
|
||||
RuningState m_ExpectedRunningState = RuningState::Stopped;
|
||||
std::unique_ptr<QProcess> m_pCoreProcess;
|
||||
QMenuBar *m_pMenuBar = nullptr;
|
||||
QMenu *m_pMenuFile = nullptr;
|
||||
QMenu *m_pMenuEdit = nullptr;
|
||||
QMenu *m_pMenuWindow = nullptr;
|
||||
QMenu *m_pMenuHelp = nullptr;
|
||||
QAbstractButton *m_pCancelButton = nullptr;
|
||||
CoreState m_CoreState = CoreState::Disconnected;
|
||||
bool m_AlreadyHidden = false;
|
||||
bool m_SecureSocket = false;
|
||||
QString m_SecureSocketVersion = "";
|
||||
bool m_SaveWindow = false;
|
||||
LicenseHandler m_LicenseHandler;
|
||||
|
||||
synergy::gui::ConfigScopes &m_ConfigScopes;
|
||||
AppConfig &m_AppConfig;
|
||||
ServerConfig m_ServerConfig;
|
||||
ServerConnection m_ServerConnection;
|
||||
ClientConnection m_ClientConnection;
|
||||
synergy::gui::CoreProcess m_CoreProcess;
|
||||
synergy::gui::ServerConnection m_ServerConnection;
|
||||
synergy::gui::ClientConnection m_ClientConnection;
|
||||
synergy::gui::TlsUtility m_TlsUtility;
|
||||
QTimer m_WindowSaveTimer;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -25,16 +25,13 @@
|
|||
<property name="windowTitle">
|
||||
<string>Synergy</string>
|
||||
</property>
|
||||
<widget class="QWidget" name="centralwidget">
|
||||
<layout class="QVBoxLayout" name="verticalLayout_2">
|
||||
<widget class="QWidget" name="m_pWidgetTopLevel">
|
||||
<layout class="QVBoxLayout" name="verticalLayout_7">
|
||||
<property name="spacing">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>20</number>
|
||||
<number>10</number>
|
||||
</property>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="m_layout1">
|
||||
<layout class="QHBoxLayout" name="m_layoutName">
|
||||
<item>
|
||||
<widget class="QLabel" name="m_pLabelComputerName">
|
||||
<property name="text">
|
||||
|
|
@ -67,22 +64,6 @@
|
|||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="verticalSpacer_5">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
<property name="sizeType">
|
||||
<enum>QSizePolicy::Fixed</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>20</width>
|
||||
<height>10</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="m_pLabelIpAddresses">
|
||||
<property name="toolTip">
|
||||
|
|
@ -94,7 +75,7 @@
|
|||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QWidget" name="m_pMainWidgets" native="true">
|
||||
<widget class="QWidget" name="m_pWidgetModes" native="true">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
|
|
@ -102,6 +83,21 @@
|
|||
</sizepolicy>
|
||||
</property>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_8">
|
||||
<property name="spacing">
|
||||
<number>20</number>
|
||||
</property>
|
||||
<property name="leftMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QGroupBox" name="m_pGroupServer">
|
||||
<property name="sizePolicy">
|
||||
|
|
@ -116,6 +112,9 @@
|
|||
</property>
|
||||
<item>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_3">
|
||||
<property name="spacing">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QRadioButton" name="m_pRadioGroupServer">
|
||||
<property name="text">
|
||||
|
|
@ -281,6 +280,9 @@
|
|||
</property>
|
||||
<item>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_6">
|
||||
<property name="spacing">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QRadioButton" name="m_pRadioGroupClient">
|
||||
<property name="text">
|
||||
|
|
@ -388,41 +390,15 @@
|
|||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="m_pLabelLogs">
|
||||
<property name="text">
|
||||
<string>Logs</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="verticalSpacer_7">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
<property name="sizeType">
|
||||
<enum>QSizePolicy::Fixed</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>20</width>
|
||||
<height>5</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QFrame" name="m_pGroupLog">
|
||||
<widget class="QGroupBox" name="m_pGroupLog">
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>0</width>
|
||||
<height>0</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="frameShape">
|
||||
<enum>QFrame::StyledPanel</enum>
|
||||
</property>
|
||||
<property name="frameShadow">
|
||||
<enum>QFrame::Raised</enum>
|
||||
<property name="title">
|
||||
<string>Logs</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_4">
|
||||
<property name="leftMargin">
|
||||
|
|
@ -469,16 +445,13 @@
|
|||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout">
|
||||
<layout class="QHBoxLayout" name="m_pLayoutActions">
|
||||
<property name="spacing">
|
||||
<number>6</number>
|
||||
</property>
|
||||
<property name="sizeConstraint">
|
||||
<enum>QLayout::SetDefaultConstraint</enum>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>16</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QLabel" name="m_pLabelPadlock">
|
||||
<property name="enabled">
|
||||
|
|
|
|||
|
|
@ -24,4 +24,8 @@
|
|||
#include <QtGui>
|
||||
|
||||
QSynergyApplication::QSynergyApplication(int &argc, char **argv)
|
||||
: QApplication(argc, argv) {}
|
||||
: QApplication(argc, argv) {
|
||||
|
||||
// causes dark mode to be used on some OS (e.g. Windows)
|
||||
setStyle("fusion");
|
||||
}
|
||||
|
|
|
|||
|
|
@ -27,6 +27,8 @@
|
|||
#include <QtCore>
|
||||
#include <QtGui>
|
||||
|
||||
using namespace synergy::gui;
|
||||
|
||||
ScreenSettingsDialog::ScreenSettingsDialog(
|
||||
QWidget *parent, Screen *pScreen, const ScreenList *pScreens)
|
||||
: QDialog(parent, Qt::WindowTitleHint | Qt::WindowSystemMenuHint),
|
||||
|
|
|
|||
|
|
@ -340,8 +340,7 @@ int ServerConfig::autoAddScreen(const QString name) {
|
|||
}
|
||||
|
||||
if (findScreenName(name, targetIndex)) {
|
||||
m_pMainWindow->appendLogDebug(
|
||||
QString("ignoring screen already in config: %1").arg(name));
|
||||
qDebug("ignoring screen already in config: %s", qPrintable(name));
|
||||
return kAutoAddScreenIgnore;
|
||||
}
|
||||
|
||||
|
|
@ -428,7 +427,7 @@ bool ServerConfig::isFull() const {
|
|||
return isFull;
|
||||
}
|
||||
|
||||
bool ServerConfig::isScreenExists(const QString &screenName) const {
|
||||
bool ServerConfig::screenExists(const QString &screenName) const {
|
||||
bool isExists = false;
|
||||
|
||||
for (const auto &screen : screens()) {
|
||||
|
|
|
|||
|
|
@ -21,6 +21,7 @@
|
|||
#include "Hotkey.h"
|
||||
#include "ScreenConfig.h"
|
||||
#include "ScreenList.h"
|
||||
#include "gui/config/IServerConfig.h"
|
||||
|
||||
#include <QList>
|
||||
|
||||
|
|
@ -35,9 +36,8 @@ class ServerConfigDialog;
|
|||
class MainWindow;
|
||||
class AppConfig;
|
||||
|
||||
class ServerConfig : public ScreenConfig {
|
||||
class ServerConfig : public ScreenConfig, public synergy::gui::IServerConfig {
|
||||
friend class ServerConfigDialog;
|
||||
friend class ServerConnection;
|
||||
friend QTextStream &
|
||||
operator<<(QTextStream &outStream, const ServerConfig &config);
|
||||
|
||||
|
|
@ -45,7 +45,7 @@ public:
|
|||
ServerConfig(
|
||||
AppConfig &appConfig, MainWindow &mainWindow,
|
||||
int columns = kDefaultColumns, int rows = kDefaultRows);
|
||||
~ServerConfig() = default;
|
||||
~ServerConfig() override = default;
|
||||
|
||||
bool operator==(const ServerConfig &sc) const;
|
||||
|
||||
|
|
@ -64,23 +64,23 @@ public:
|
|||
int switchCornerSize() const { return m_SwitchCornerSize; }
|
||||
const QList<bool> &switchCorners() const { return m_SwitchCorners; }
|
||||
const HotkeyList &hotkeys() const { return m_Hotkeys; }
|
||||
bool enableDragAndDrop() const { return m_EnableDragAndDrop; }
|
||||
bool enableDragAndDrop() const override { return m_EnableDragAndDrop; }
|
||||
bool disableLockToScreen() const { return m_DisableLockToScreen; }
|
||||
bool clipboardSharing() const { return m_ClipboardSharing; }
|
||||
size_t clipboardSharingSize() const { return m_ClipboardSharingSize; }
|
||||
static size_t defaultClipboardSharingSize();
|
||||
|
||||
void commit();
|
||||
bool save(const QString &fileName) const;
|
||||
void save(QFile &file) const;
|
||||
bool save(const QString &fileName) const override;
|
||||
void save(QFile &file) const override;
|
||||
int numScreens() const;
|
||||
int autoAddScreen(const QString name);
|
||||
const QString &getServerName() const;
|
||||
void updateServerName();
|
||||
const QString &configFile() const;
|
||||
bool useExternalConfig() const;
|
||||
bool isFull() const;
|
||||
bool isScreenExists(const QString &screenName) const;
|
||||
bool isFull() const override;
|
||||
bool screenExists(const QString &screenName) const override;
|
||||
void addClient(const QString &clientName);
|
||||
QString getClientAddress() const;
|
||||
void setClientAddress(const QString &address);
|
||||
|
|
|
|||
|
|
@ -22,7 +22,7 @@
|
|||
|
||||
#include "ScreenSetupModel.h"
|
||||
#include "ServerConfig.h"
|
||||
#include "gui/AppConfig.h"
|
||||
#include "gui/config/AppConfig.h"
|
||||
#include "ui_ServerConfigDialogBase.h"
|
||||
|
||||
#include <QDialog>
|
||||
|
|
@ -36,9 +36,9 @@ public:
|
|||
bool addClient(const QString &clientName);
|
||||
|
||||
public slots:
|
||||
void accept();
|
||||
void accept() override;
|
||||
void reject() override;
|
||||
void showEvent(QShowEvent *event);
|
||||
void showEvent(QShowEvent *event) override;
|
||||
void message(const QString &message) { m_Message = message; }
|
||||
|
||||
protected slots:
|
||||
|
|
|
|||
|
|
@ -76,7 +76,7 @@
|
|||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="horizontalSpacer_2">
|
||||
<spacer name="horizontalSpacer_21">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
|
|
@ -117,7 +117,7 @@
|
|||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="horizontalSpacer_3">
|
||||
<spacer name="horizontalSpacer_22">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
|
|
@ -342,7 +342,7 @@
|
|||
<number>9</number>
|
||||
</property>
|
||||
<item row="1" column="0">
|
||||
<layout class="QVBoxLayout" name="verticalLayout_2">
|
||||
<layout class="QVBoxLayout" name="verticalLayout_21">
|
||||
<property name="spacing">
|
||||
<number>0</number>
|
||||
</property>
|
||||
|
|
@ -482,7 +482,7 @@
|
|||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="verticalSpacer_4">
|
||||
<spacer name="verticalSpacer_41">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
|
|
@ -644,7 +644,7 @@
|
|||
</widget>
|
||||
</item>
|
||||
<item row="1" column="5">
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_2">
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_21">
|
||||
<item>
|
||||
<widget class="QLabel" name="label">
|
||||
<property name="text">
|
||||
|
|
@ -1105,7 +1105,7 @@ Enabling this setting will disable the server config GUI.</string>
|
|||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="verticalSpacer">
|
||||
<spacer name="verticalSpacer_1">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
|
|
|
|||
|
|
@ -1,78 +0,0 @@
|
|||
/*
|
||||
* synergy -- mouse and keyboard sharing utility
|
||||
* Copyright (C) 2012-2021 Symless Ltd.
|
||||
* Copyright (C) 2008 Volker Lanz (vl@fidra.de)
|
||||
*
|
||||
* 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 "ServerConnection.h"
|
||||
|
||||
#include "MainWindow.h"
|
||||
#include "ServerConfigDialog.h"
|
||||
#include "ServerMessage.h"
|
||||
#include "gui/AppConfig.h"
|
||||
|
||||
#include <QMessageBox>
|
||||
|
||||
ServerConnection::ServerConnection(MainWindow &parent) : m_parent(parent) {}
|
||||
|
||||
void ServerConnection::update(const QString &line) {
|
||||
ServerMessage message(line);
|
||||
|
||||
if (!m_parent.appConfig().useExternalConfig() &&
|
||||
message.isNewClientMessage() &&
|
||||
!m_ignoredClients.contains(message.getClientName())) {
|
||||
addClient(message.getClientName());
|
||||
}
|
||||
}
|
||||
|
||||
bool ServerConnection::checkMainWindow() {
|
||||
bool result = m_parent.isActiveWindow();
|
||||
|
||||
if (m_parent.isMinimized() || m_parent.isHidden()) {
|
||||
m_parent.showNormal();
|
||||
m_parent.activateWindow();
|
||||
result = true;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
void ServerConnection::addClient(const QString &clientName) {
|
||||
if (!m_parent.serverConfig().isFull() &&
|
||||
!m_parent.serverConfig().isScreenExists(clientName) &&
|
||||
checkMainWindow()) {
|
||||
QMessageBox message(&m_parent);
|
||||
message.addButton(QObject::tr("Ignore"), QMessageBox::RejectRole);
|
||||
message.addButton(
|
||||
QObject::tr("Accept and configure"), QMessageBox::AcceptRole);
|
||||
message.setText(
|
||||
QObject::tr("%1 client has made a connection request").arg(clientName));
|
||||
|
||||
if (message.exec() == QMessageBox::Accepted) {
|
||||
configureClient(clientName);
|
||||
} else {
|
||||
m_ignoredClients.append(clientName);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ServerConnection::configureClient(const QString &clientName) {
|
||||
ServerConfigDialog dialog(
|
||||
&m_parent, m_parent.serverConfig(), m_parent.appConfig());
|
||||
|
||||
if (dialog.addClient(clientName) && dialog.exec() == QDialog::Accepted) {
|
||||
m_parent.restartCore();
|
||||
}
|
||||
}
|
||||
|
|
@ -20,9 +20,10 @@
|
|||
|
||||
#include "MainWindow.h"
|
||||
#include "UpgradeDialog.h"
|
||||
#include "gui/AppConfig.h"
|
||||
#include "gui/TlsCertificate.h"
|
||||
#include "gui/config/AppConfig.h"
|
||||
#include "gui/constants.h"
|
||||
#include "gui/tls/TlsCertificate.h"
|
||||
#include "gui/tls/TlsUtility.h"
|
||||
#include "validators/ScreenNameValidator.h"
|
||||
#include "validators/ValidationError.h"
|
||||
|
||||
|
|
@ -31,8 +32,10 @@
|
|||
#include <QMessageBox>
|
||||
#include <QtCore>
|
||||
#include <QtGui>
|
||||
#include <qglobal.h>
|
||||
|
||||
using namespace synergy::license;
|
||||
using namespace synergy::gui;
|
||||
|
||||
const char *const kProProductName = "Synergy 1 Pro";
|
||||
|
||||
|
|
@ -82,7 +85,7 @@ void SettingsDialog::accept() {
|
|||
appConfig().setAutoHide(m_pCheckBoxAutoHide->isChecked());
|
||||
appConfig().setPreventSleep(m_pCheckBoxPreventSleep->isChecked());
|
||||
appConfig().setTlsCertPath(m_pLineEditCertificatePath->text());
|
||||
appConfig().setTlsKeyLength(m_pComboBoxKeyLength->currentText());
|
||||
appConfig().setTlsKeyLength(m_pComboBoxKeyLength->currentText().toInt());
|
||||
appConfig().setTlsEnabled(m_pCheckBoxEnableCrypto->isChecked());
|
||||
appConfig().setLanguageSync(m_pCheckBoxLanguageSync->isChecked());
|
||||
appConfig().setInvertScrollDirection(m_pCheckBoxScrollDirection->isChecked());
|
||||
|
|
@ -136,8 +139,9 @@ void SettingsDialog::updateTlsControls() {
|
|||
if (QFile(appConfig().tlsCertPath()).exists()) {
|
||||
updateKeyLengthOnFile(appConfig().tlsCertPath());
|
||||
} else {
|
||||
const auto keyLengthText = QString::number(appConfig().tlsKeyLength());
|
||||
m_pComboBoxKeyLength->setCurrentIndex(
|
||||
m_pComboBoxKeyLength->findText(appConfig().tlsKeyLength()));
|
||||
m_pComboBoxKeyLength->findText(keyLengthText));
|
||||
}
|
||||
|
||||
m_pCheckBoxEnableCrypto->setChecked(m_appConfig.tlsEnabled());
|
||||
|
|
@ -189,7 +193,7 @@ void SettingsDialog::on_m_pButtonBrowseLog_clicked() {
|
|||
void SettingsDialog::on_m_pCheckBoxEnableCrypto_clicked(bool) {
|
||||
updateTlsControlsEnabled();
|
||||
|
||||
if (kLicensingEnabled && !m_tlsUtility.isAvailable()) {
|
||||
if (kEnableActivation && !m_tlsUtility.isAvailable()) {
|
||||
auto edition = m_license.productEdition();
|
||||
if (edition == Edition::kBasic) {
|
||||
UpgradeDialog upgradeDialog(this);
|
||||
|
|
@ -215,9 +219,11 @@ void SettingsDialog::on_m_pPushButtonBrowseCert_clicked() {
|
|||
|
||||
if (!fileName.isEmpty()) {
|
||||
m_pLineEditCertificatePath->setText(fileName);
|
||||
// If the tls file exists test its key length and update
|
||||
if (QFile(appConfig().tlsCertPath()).exists()) {
|
||||
|
||||
if (QFile(fileName).exists()) {
|
||||
updateKeyLengthOnFile(fileName);
|
||||
} else {
|
||||
qDebug("no tls certificate file at: %s", qUtf8Printable(fileName));
|
||||
}
|
||||
}
|
||||
updateTlsRegenerateButton();
|
||||
|
|
@ -228,9 +234,8 @@ void SettingsDialog::on_m_pComboBoxKeyLength_currentIndexChanged(int index) {
|
|||
}
|
||||
|
||||
void SettingsDialog::updateTlsRegenerateButton() {
|
||||
// Disable the Regenerate cert button if the key length is different to saved
|
||||
auto keyChanged =
|
||||
appConfig().tlsKeyLength() != m_pComboBoxKeyLength->currentText();
|
||||
const auto keyLength = m_pComboBoxKeyLength->currentText().toInt();
|
||||
auto keyChanged = appConfig().tlsKeyLength() != keyLength;
|
||||
auto pathChanged =
|
||||
appConfig().tlsCertPath() != m_pLineEditCertificatePath->text();
|
||||
// NOR the above bools, if any have changed regen should be disabled as it
|
||||
|
|
@ -241,15 +246,22 @@ void SettingsDialog::updateTlsRegenerateButton() {
|
|||
}
|
||||
|
||||
void SettingsDialog::on_m_pPushButtonRegenCert_clicked() {
|
||||
m_tlsUtility.generateCertificate(true);
|
||||
if (m_tlsUtility.generateCertificate()) {
|
||||
QMessageBox::information(
|
||||
this, tr("TLS Certificate Regenerated"),
|
||||
tr("TLS certificate regenerated successfully."));
|
||||
}
|
||||
}
|
||||
|
||||
void SettingsDialog::updateKeyLengthOnFile(const QString &path) {
|
||||
TlsCertificate ssl;
|
||||
if (!QFile(path).exists()) {
|
||||
qFatal("tls certificate file not found: %s", qUtf8Printable(path));
|
||||
}
|
||||
|
||||
auto length = ssl.getCertKeyLength(path);
|
||||
auto index = m_pComboBoxKeyLength->findText(length);
|
||||
auto index = m_pComboBoxKeyLength->findText(QString::number(length));
|
||||
m_pComboBoxKeyLength->setCurrentIndex(index);
|
||||
// Also update what is in the appconfig to match the file itself
|
||||
appConfig().setTlsKeyLength(length);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -20,8 +20,8 @@
|
|||
|
||||
#include "ui_SettingsDialogBase.h"
|
||||
|
||||
#include "gui/CoreInterface.h"
|
||||
#include "gui/TlsUtility.h"
|
||||
#include "gui/core/CoreTool.h"
|
||||
#include "gui/tls/TlsUtility.h"
|
||||
#include "license/License.h"
|
||||
#include "validators/ValidationError.h"
|
||||
|
||||
|
|
@ -68,7 +68,7 @@ protected:
|
|||
private:
|
||||
MainWindow *m_pMainWindow;
|
||||
AppConfig &m_appConfig;
|
||||
CoreInterface m_coreInterface;
|
||||
[[no_unique_address]] CoreTool m_coreTool;
|
||||
const synergy::license::License &m_license;
|
||||
synergy::gui::TlsUtility m_tlsUtility;
|
||||
validators::ValidationError *m_pScreenNameError;
|
||||
|
|
|
|||
|
|
@ -21,6 +21,8 @@
|
|||
#include "validators/ScreenNameValidator.h"
|
||||
#include "validators/ValidationError.h"
|
||||
|
||||
using namespace synergy::gui;
|
||||
|
||||
SetupWizard::SetupWizard(AppConfig &appConfig) : m_appConfig(appConfig) {
|
||||
setupUi(this);
|
||||
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@
|
|||
|
||||
#include "ui_SetupWizardBase.h"
|
||||
|
||||
#include "gui/AppConfig.h"
|
||||
#include "gui/config/AppConfig.h"
|
||||
|
||||
#include <QDialog>
|
||||
#include <QObject>
|
||||
|
|
|
|||
|
|
@ -1,35 +0,0 @@
|
|||
/*
|
||||
* synergy -- mouse and keyboard sharing utility
|
||||
* Copyright (C) 2021 Symless Ltd.
|
||||
*
|
||||
* This package is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* 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 "TrayIcon.h"
|
||||
|
||||
void TrayIcon::tryCreate() const {
|
||||
QSystemTrayIcon trayIcon; // by creating a new tray icon, we actually make the
|
||||
// DBus implementation refresh the connection (DBus)
|
||||
trayIcon.hide(); // we ony hide it in order for the compiler to not
|
||||
// optimise-out the object (make some use of it)
|
||||
if (QSystemTrayIcon::isSystemTrayAvailable()) { // this ends up calling the
|
||||
// underlying DBus connection
|
||||
// (on DBus)
|
||||
m_pTrayIcon->show();
|
||||
m_connector(
|
||||
m_pTrayIcon.get(),
|
||||
SIGNAL(activated(QSystemTrayIcon::ActivationReason)));
|
||||
} else {
|
||||
QTimer::singleShot(2500, this, &TrayIcon::tryCreate);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,73 +0,0 @@
|
|||
/*
|
||||
* synergy -- mouse and keyboard sharing utility
|
||||
* Copyright (C) 2021 Symless Ltd.
|
||||
*
|
||||
* This package is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* 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 <QMenu>
|
||||
#include <QObject>
|
||||
#include <QSystemTrayIcon>
|
||||
#include <QTimer>
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
|
||||
class TrayIcon : public QObject {
|
||||
Q_OBJECT
|
||||
public:
|
||||
using TConnector = std::function<void(QObject const *, const char *)>;
|
||||
|
||||
TrayIcon() {
|
||||
m_set = [this](const QIcon &icon) {
|
||||
m_init = [this, icon]() { this->set(icon); };
|
||||
};
|
||||
}
|
||||
|
||||
template <typename TActionContainer>
|
||||
void
|
||||
create(TActionContainer const &actionContainer, TConnector const &connector) {
|
||||
m_connector = connector;
|
||||
m_pTrayIconMenu = std::make_unique<QMenu>();
|
||||
|
||||
for (auto action : actionContainer) {
|
||||
if (action) {
|
||||
m_pTrayIconMenu->addAction(action);
|
||||
} else {
|
||||
m_pTrayIconMenu->addSeparator();
|
||||
}
|
||||
}
|
||||
|
||||
m_pTrayIcon = std::make_unique<QSystemTrayIcon>();
|
||||
m_pTrayIcon->setContextMenu(m_pTrayIconMenu.get());
|
||||
m_pTrayIcon->setToolTip("Synergy");
|
||||
m_set = [this](const QIcon &icon) { m_pTrayIcon->setIcon(icon); };
|
||||
|
||||
tryCreate();
|
||||
if (m_init) {
|
||||
m_init();
|
||||
m_init = std::function<void()>();
|
||||
}
|
||||
}
|
||||
|
||||
void tryCreate() const;
|
||||
void set(const QIcon &icon) const { m_set(icon); }
|
||||
|
||||
private:
|
||||
std::unique_ptr<QSystemTrayIcon> m_pTrayIcon{};
|
||||
std::unique_ptr<QMenu> m_pTrayIconMenu{};
|
||||
TConnector m_connector;
|
||||
std::function<void()> m_init;
|
||||
std::function<void(const QIcon &icon)> m_set;
|
||||
};
|
||||
|
|
@ -20,8 +20,9 @@
|
|||
#include "QSynergyApplication.h"
|
||||
#include "SetupWizard.h"
|
||||
#include "SetupWizardBlocker.h"
|
||||
#include "gui/AppConfig.h"
|
||||
#include "gui/ConfigScopes.h"
|
||||
#include "gui/Logger.h"
|
||||
#include "gui/config/AppConfig.h"
|
||||
#include "gui/config/ConfigScopes.h"
|
||||
#include "gui/constants.h"
|
||||
#include "gui/dotenv.h"
|
||||
#include "gui/messages.h"
|
||||
|
|
@ -54,7 +55,7 @@ bool checkMacAssistiveDevices();
|
|||
|
||||
int main(int argc, char *argv[]) {
|
||||
|
||||
#ifdef Q_OS_DARWIN
|
||||
#if defined(Q_OS_MAC)
|
||||
/* Workaround for QTBUG-40332 - "High ping when QNetworkAccessManager is
|
||||
* instantiated" */
|
||||
::setenv("QT_BEARER_POLL_TIMEOUT", "-1", 1);
|
||||
|
|
@ -67,9 +68,10 @@ int main(int argc, char *argv[]) {
|
|||
QSynergyApplication app(argc, argv);
|
||||
qInstallMessageHandler(synergy::gui::messages::messageHandler);
|
||||
|
||||
qInfo("Synergy v%s", synergy::gui::version().toUtf8().constData());
|
||||
|
||||
dotenv(".env");
|
||||
Logger::instance().loadEnvVars();
|
||||
|
||||
qInfo("Synergy v%s", synergy::gui::version().toUtf8().constData());
|
||||
|
||||
#if defined(Q_OS_MAC)
|
||||
|
||||
|
|
|
|||
|
|
@ -18,7 +18,10 @@
|
|||
#include "LineEditValidator.h"
|
||||
|
||||
#include "gui/styles.h"
|
||||
#include <qvalidator.h>
|
||||
|
||||
#include <QValidator>
|
||||
|
||||
using namespace synergy::gui;
|
||||
|
||||
namespace validators {
|
||||
|
||||
|
|
|
|||
|
|
@ -19,6 +19,8 @@
|
|||
|
||||
#include "gui/styles.h"
|
||||
|
||||
using namespace synergy::gui;
|
||||
|
||||
namespace validators {
|
||||
|
||||
void clear(QLabel *label) {
|
||||
|
|
|
|||
|
|
@ -18,8 +18,8 @@
|
|||
#pragma once
|
||||
|
||||
#include <QLabel>
|
||||
#include <QObject>
|
||||
#include <QString>
|
||||
#include <qobject.h>
|
||||
|
||||
namespace validators {
|
||||
|
||||
|
|
|
|||
|
|
@ -18,7 +18,9 @@
|
|||
|
||||
#include "ServerStateLabel.h"
|
||||
|
||||
#include "ServerMessage.h"
|
||||
#include "gui/core/ServerMessage.h"
|
||||
|
||||
using namespace synergy::gui;
|
||||
|
||||
namespace synergy_widgets {
|
||||
|
||||
|
|
@ -38,7 +40,14 @@ void ServerStateLabel::updateServerState(const QString &line) {
|
|||
if (m_clients.isEmpty()) {
|
||||
setText(tr("No clients connected"));
|
||||
} else {
|
||||
setText(tr("Connected clients: %1").arg(m_clients.join(", ")));
|
||||
// unfortunately, we can't rely on the clients list because we don't always
|
||||
// catch the connect/disconnect messages. so clients tend to get stuck in
|
||||
// the list even though they're offline.
|
||||
// in order to properly show a list of clients, we would need the core to
|
||||
// print a list of connected clients on every connect/disconnect event,
|
||||
// which could be a bit noisy in the logs (perhaps an ipc message would be
|
||||
// needed).
|
||||
setText(tr("Client(s) are connected"));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -23,6 +23,11 @@ set(CMAKE_INCLUDE_CURRENT_DIR ON)
|
|||
file(GLOB_RECURSE sources *.cpp)
|
||||
file(GLOB_RECURSE headers *.h)
|
||||
|
||||
if(${CMAKE_SYSTEM_NAME} MATCHES "Darwin")
|
||||
file(GLOB mac_sources *.mm)
|
||||
list(APPEND sources ${mac_sources})
|
||||
endif()
|
||||
|
||||
if(ADD_HEADERS_TO_SOURCES)
|
||||
list(APPEND sources ${headers})
|
||||
endif()
|
||||
|
|
|
|||
|
|
@ -1,35 +0,0 @@
|
|||
/*
|
||||
* synergy -- mouse and keyboard sharing utility
|
||||
* Copyright (C) 2016 Symless
|
||||
*
|
||||
* 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
|
||||
|
||||
// The elevate mode tristate determines two behaviours on Windows.
|
||||
// The first, switch-on-desk-switch (SodS), passed through synergyd as a
|
||||
// command line argument to synergy core, determines if the server restarts
|
||||
// when switching Windows desktops (e.g. when Windows UAC dialog pops up).
|
||||
// The second, passed as a boolean flag to Synergyd over the IPC inside
|
||||
// IpcMessageType::CommandMessage, determines whether Synergy should be started
|
||||
// with elevated privileges.
|
||||
//
|
||||
// The matrix for these two behaviours is as follows:
|
||||
// SodS Elevate
|
||||
// ___________________________
|
||||
// ElevateAsNeeded | true | false
|
||||
// ElevateAlways | false | true
|
||||
// ElevateNever | false | false
|
||||
//
|
||||
enum ElevateMode { ElevateAsNeeded = 0, ElevateAlways = 1, ElevateNever = 2 };
|
||||
100
src/lib/gui/Logger.cpp
Normal file
100
src/lib/gui/Logger.cpp
Normal file
|
|
@ -0,0 +1,100 @@
|
|||
/*
|
||||
* 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 "Logger.h"
|
||||
|
||||
#include "string_utils.h"
|
||||
|
||||
#include <QDateTime>
|
||||
#include <QDir>
|
||||
#include <QMessageBox>
|
||||
#include <QTime>
|
||||
|
||||
namespace synergy::gui {
|
||||
|
||||
Logger Logger::s_instance;
|
||||
|
||||
QString printLine(
|
||||
FILE *out, const QString &type, const QString &message,
|
||||
const QString &fileLine = "") {
|
||||
auto datetime = QDateTime::currentDateTime().toString("yyyy-MM-ddTHH:mm:ss");
|
||||
auto logLine = QString("[%1] %2: %3").arg(datetime).arg(type).arg(message);
|
||||
|
||||
if (!fileLine.isEmpty()) {
|
||||
logLine += "\n\t" + fileLine;
|
||||
}
|
||||
|
||||
auto logLineUtf = logLine.toUtf8();
|
||||
auto logLine_c = logLineUtf.constData();
|
||||
fprintf(out, "%s\n", logLine_c);
|
||||
fflush(out);
|
||||
return logLine;
|
||||
}
|
||||
|
||||
void Logger::loadEnvVars() {
|
||||
const auto debugEnvVar = qEnvironmentVariable("SYNERGY_GUI_DEBUG");
|
||||
if (!debugEnvVar.isEmpty()) {
|
||||
m_debug = strToTrue(debugEnvVar);
|
||||
}
|
||||
|
||||
const auto verboseEnvVar = qEnvironmentVariable("SYNERGY_GUI_VERBOSE");
|
||||
if (!verboseEnvVar.isEmpty()) {
|
||||
m_verbose = strToTrue(verboseEnvVar);
|
||||
}
|
||||
}
|
||||
|
||||
void Logger::logVerbose(const QString &message) const {
|
||||
if (m_verbose) {
|
||||
printLine(stdout, "VERBOSE", message);
|
||||
}
|
||||
}
|
||||
|
||||
void Logger::handleMessage(
|
||||
QtMsgType type, const QMessageLogContext &context, const QString &message) {
|
||||
|
||||
QString typeString;
|
||||
auto out = stdout;
|
||||
switch (type) {
|
||||
case QtDebugMsg:
|
||||
typeString = "DEBUG";
|
||||
if (!m_debug) {
|
||||
return;
|
||||
}
|
||||
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;
|
||||
}
|
||||
|
||||
const auto fileLine = QString("%1:%2").arg(context.file).arg(context.line);
|
||||
const auto logLine = printLine(out, typeString, message, fileLine);
|
||||
emit newLine(logLine);
|
||||
}
|
||||
|
||||
} // namespace synergy::gui
|
||||
55
src/lib/gui/Logger.h
Normal file
55
src/lib/gui/Logger.h
Normal file
|
|
@ -0,0 +1,55 @@
|
|||
/*
|
||||
* 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 <QObject>
|
||||
|
||||
#ifdef NDEBUG
|
||||
const bool kDebug = false;
|
||||
#else
|
||||
const bool kDebug = true;
|
||||
#endif
|
||||
|
||||
namespace synergy::gui {
|
||||
|
||||
class Logger : public QObject {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
static Logger &instance() { return s_instance; }
|
||||
|
||||
void loadEnvVars();
|
||||
void handleMessage(
|
||||
QtMsgType type, const QMessageLogContext &context,
|
||||
const QString &message);
|
||||
void logVerbose(const QString &message) const;
|
||||
|
||||
signals:
|
||||
void newLine(const QString &line);
|
||||
|
||||
private:
|
||||
static Logger s_instance;
|
||||
bool m_debug = kDebug;
|
||||
bool m_verbose = false;
|
||||
};
|
||||
|
||||
inline void logVerbose(const QString &message) {
|
||||
Logger::instance().logVerbose(message);
|
||||
}
|
||||
|
||||
} // namespace synergy::gui
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
/*
|
||||
* synergy -- mouse and keyboard sharing utility
|
||||
* Copyright (C) 2015 Synergy Si Ltd.
|
||||
* Copyright (C) 2015 Symless Ltd.
|
||||
*
|
||||
* This package is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
|
|
@ -15,9 +15,7 @@
|
|||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#if !defined(OSXHELPERS__H)
|
||||
|
||||
#define OSXHELPERS__H
|
||||
#pragma once
|
||||
|
||||
#include <QString>
|
||||
|
||||
|
|
@ -28,5 +26,3 @@ bool isOSXDevelopmentBuild();
|
|||
bool showOSXNotification(const QString &title, const QString &body);
|
||||
bool isOSXInterfaceStyleDark();
|
||||
IconsTheme getOSXIconsTheme();
|
||||
|
||||
#endif
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
/*
|
||||
* synergy -- mouse and keyboard sharing utility
|
||||
* Copyright (C) 2015 Synergy Si Ltd.
|
||||
* Copyright (C) 2015 Symless Ltd.
|
||||
*
|
||||
* This package is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
|
|
@ -1,155 +0,0 @@
|
|||
/*
|
||||
* synergy -- mouse and keyboard sharing utility
|
||||
* Copyright (C) 2012 Symless Ltd.
|
||||
*
|
||||
* This package is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* 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 "QIpcClient.h"
|
||||
|
||||
#include "IpcReader.h"
|
||||
#include "global/Ipc.h"
|
||||
|
||||
#include <QDataStream>
|
||||
#include <QHostAddress>
|
||||
#include <QTimer>
|
||||
|
||||
QIpcClient::QIpcClient(const StreamProvider &streamProvider)
|
||||
: m_ReaderStarted(false),
|
||||
m_Enabled(false),
|
||||
m_StreamProvider(streamProvider) {
|
||||
|
||||
m_Socket = new QTcpSocket(this);
|
||||
|
||||
if (!m_StreamProvider) {
|
||||
m_StreamProvider = [this]() {
|
||||
return std::make_shared<QDataStreamProxy>(m_Socket);
|
||||
};
|
||||
}
|
||||
|
||||
connect(m_Socket, SIGNAL(connected()), this, SLOT(connected()));
|
||||
connect(
|
||||
m_Socket, SIGNAL(errorOccurred(QAbstractSocket::SocketError)), this,
|
||||
SLOT(error(QAbstractSocket::SocketError)));
|
||||
|
||||
m_Reader = new IpcReader(m_Socket);
|
||||
connect(
|
||||
m_Reader, SIGNAL(readLogLine(const QString &)), this,
|
||||
SLOT(handleReadLogLine(const QString &)));
|
||||
}
|
||||
|
||||
QIpcClient::~QIpcClient() {
|
||||
delete m_Reader;
|
||||
delete m_Socket;
|
||||
}
|
||||
|
||||
void QIpcClient::connected() {
|
||||
|
||||
sendHello();
|
||||
emit infoMessage("connection established");
|
||||
}
|
||||
|
||||
void QIpcClient::connectToHost() {
|
||||
m_Enabled = true;
|
||||
|
||||
emit infoMessage("connecting to service...");
|
||||
const auto port = static_cast<quint16>(kIpcPort);
|
||||
m_Socket->connectToHost(QHostAddress(QHostAddress::LocalHost), port);
|
||||
|
||||
if (!m_ReaderStarted) {
|
||||
m_Reader->start();
|
||||
m_ReaderStarted = true;
|
||||
}
|
||||
}
|
||||
|
||||
void QIpcClient::disconnectFromHost() {
|
||||
emit infoMessage("service disconnect");
|
||||
m_Reader->stop();
|
||||
m_Socket->close();
|
||||
}
|
||||
|
||||
void QIpcClient::error(QAbstractSocket::SocketError error) {
|
||||
QString text;
|
||||
switch (error) {
|
||||
case 0:
|
||||
text = "connection refused";
|
||||
break;
|
||||
case 1:
|
||||
text = "remote host closed";
|
||||
break;
|
||||
default:
|
||||
text = QString("code=%1").arg(error);
|
||||
break;
|
||||
}
|
||||
|
||||
emit errorMessage(QString("ipc connection error, %1").arg(text));
|
||||
|
||||
QTimer::singleShot(1000, this, SLOT(retryConnect()));
|
||||
}
|
||||
|
||||
void QIpcClient::retryConnect() {
|
||||
if (m_Enabled) {
|
||||
connectToHost();
|
||||
}
|
||||
}
|
||||
|
||||
void QIpcClient::sendHello() {
|
||||
auto stream = m_StreamProvider();
|
||||
stream->writeRawData(kIpcMsgHello, 4);
|
||||
|
||||
char typeBuf[1];
|
||||
typeBuf[0] = static_cast<char>(IpcClientType::GUI);
|
||||
stream->writeRawData(typeBuf, 1);
|
||||
}
|
||||
|
||||
void QIpcClient::sendCommand(
|
||||
const QString &command, ElevateMode const elevate) {
|
||||
auto stream = m_StreamProvider();
|
||||
stream->writeRawData(kIpcMsgCommand, 4);
|
||||
|
||||
std::string stdStringCommand = command.toStdString();
|
||||
const char *charCommand = stdStringCommand.c_str();
|
||||
auto length = static_cast<int>(stdStringCommand.length());
|
||||
|
||||
char lenBuf[4];
|
||||
intToBytes(length, lenBuf, 4);
|
||||
stream->writeRawData(lenBuf, 4);
|
||||
stream->writeRawData(charCommand, length);
|
||||
|
||||
char elevateBuf[1];
|
||||
// Refer to enum ElevateMode documentation for why this flag is mapped this
|
||||
// way
|
||||
elevateBuf[0] = (elevate == ElevateAlways) ? 1 : 0;
|
||||
stream->writeRawData(elevateBuf, 1);
|
||||
}
|
||||
|
||||
void QIpcClient::handleReadLogLine(const QString &text) {
|
||||
emit readLogLine(text);
|
||||
}
|
||||
|
||||
// TODO: qt must have a built in way of converting int to bytes.
|
||||
void QIpcClient::intToBytes(int value, char *buffer, int size) {
|
||||
if (size == 1) {
|
||||
buffer[0] = value & 0xff;
|
||||
} else if (size == 2) {
|
||||
buffer[0] = (value >> 8) & 0xff;
|
||||
buffer[1] = value & 0xff;
|
||||
} else if (size == 4) {
|
||||
buffer[0] = (value >> 24) & 0xff;
|
||||
buffer[1] = (value >> 16) & 0xff;
|
||||
buffer[2] = (value >> 8) & 0xff;
|
||||
buffer[3] = value & 0xff;
|
||||
} else {
|
||||
// TODO: other sizes, if needed.
|
||||
}
|
||||
}
|
||||
|
|
@ -1,58 +0,0 @@
|
|||
/*
|
||||
* synergy -- mouse and keyboard sharing utility
|
||||
* Copyright (C) 2015 Symless Ltd.
|
||||
*
|
||||
* This package is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* found in the file LICENSE that should have accompanied this file.
|
||||
*
|
||||
* This package is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "gui/CoreInterface.h"
|
||||
|
||||
#include <QObject>
|
||||
|
||||
class TlsCertificate : public QObject {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit TlsCertificate(QObject *parent = nullptr);
|
||||
|
||||
public slots:
|
||||
/// @brief Generates a TLS cert and private key
|
||||
/// @param [in] QString path The path of the file to be generated
|
||||
/// @param [in] QString keyLength The size of the private key. default: 2048
|
||||
/// @param [in] bool Should the file be created regardless of if the file
|
||||
/// already exists
|
||||
void generateCertificate(
|
||||
const QString &path = QString(), const QString &keyLength = "2048",
|
||||
bool forceGen = false);
|
||||
|
||||
/// @brief Get the key length of a TLS private key
|
||||
/// @param [in] QString path The path of the file to checked
|
||||
/// @return QString The key legnth as a string
|
||||
QString getCertKeyLength(const QString &path);
|
||||
|
||||
signals:
|
||||
void error(QString e);
|
||||
void info(QString i);
|
||||
void generateFinished();
|
||||
|
||||
private:
|
||||
bool runTool(const QStringList &args);
|
||||
void generateFingerprint(const QString &certificateFilename);
|
||||
|
||||
private:
|
||||
QString m_ProfileDir;
|
||||
QString m_ToolOutput;
|
||||
CoreInterface m_CoreInterface;
|
||||
};
|
||||
75
src/lib/gui/TrayIcon.cpp
Normal file
75
src/lib/gui/TrayIcon.cpp
Normal file
|
|
@ -0,0 +1,75 @@
|
|||
/*
|
||||
* synergy -- mouse and keyboard sharing utility
|
||||
* Copyright (C) 2021 Symless Ltd.
|
||||
*
|
||||
* This package is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* 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 "TrayIcon.h"
|
||||
|
||||
#include "constants.h"
|
||||
|
||||
namespace synergy::gui {
|
||||
|
||||
const auto kShowRetryInterval = 1000;
|
||||
|
||||
void TrayIcon::setIcon(const QIcon &icon) {
|
||||
m_icon = icon;
|
||||
if (m_pTrayIcon && !icon.isNull()) {
|
||||
m_pTrayIcon->setIcon(icon);
|
||||
}
|
||||
}
|
||||
|
||||
void TrayIcon::showRetryLoop() {
|
||||
// HACK: apparently this is needed to create a dbus connection, and the hide
|
||||
// is needed to make use of the object so the dbus connection doesn't get
|
||||
// optimized away by the compiler.
|
||||
// TODO: we should verify that this hack actually works.
|
||||
QSystemTrayIcon trayIcon;
|
||||
trayIcon.hide();
|
||||
|
||||
if (QSystemTrayIcon::isSystemTrayAvailable()) {
|
||||
m_pTrayIcon->show();
|
||||
} else {
|
||||
// on some platforms, it's not always possible to create the tray when the
|
||||
// app starts, so keep trying until it is possible.
|
||||
qDebug("system tray not ready yet, retrying in %d ms", kShowRetryInterval);
|
||||
QTimer::singleShot(kShowRetryInterval, this, &TrayIcon::showRetryLoop);
|
||||
}
|
||||
}
|
||||
|
||||
void TrayIcon::create(std::vector<QAction *> const &actions) {
|
||||
m_pTrayIconMenu = std::make_unique<QMenu>();
|
||||
|
||||
for (auto action : actions) {
|
||||
if (action) {
|
||||
m_pTrayIconMenu->addAction(action);
|
||||
} else {
|
||||
m_pTrayIconMenu->addSeparator();
|
||||
}
|
||||
}
|
||||
|
||||
m_pTrayIcon = std::make_unique<QSystemTrayIcon>();
|
||||
setIcon(m_icon);
|
||||
|
||||
connect(
|
||||
m_pTrayIcon.get(), &QSystemTrayIcon::activated, this,
|
||||
&TrayIcon::activated);
|
||||
|
||||
m_pTrayIcon->setContextMenu(m_pTrayIconMenu.get());
|
||||
m_pTrayIcon->setToolTip(kAppName);
|
||||
|
||||
showRetryLoop();
|
||||
}
|
||||
|
||||
} // namespace synergy::gui
|
||||
50
src/lib/gui/TrayIcon.h
Normal file
50
src/lib/gui/TrayIcon.h
Normal file
|
|
@ -0,0 +1,50 @@
|
|||
/*
|
||||
* synergy -- mouse and keyboard sharing utility
|
||||
* Copyright (C) 2021 Symless Ltd.
|
||||
*
|
||||
* This package is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* 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 <QMenu>
|
||||
#include <QObject>
|
||||
#include <QSystemTrayIcon>
|
||||
#include <QTimer>
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
|
||||
namespace synergy::gui {
|
||||
|
||||
class TrayIcon : public QObject {
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit TrayIcon() = default;
|
||||
|
||||
void create(std::vector<QAction *> const &actions);
|
||||
void setIcon(const QIcon &icon);
|
||||
|
||||
signals:
|
||||
void activated(QSystemTrayIcon::ActivationReason reason);
|
||||
|
||||
private:
|
||||
void showRetryLoop();
|
||||
|
||||
std::unique_ptr<QSystemTrayIcon> m_pTrayIcon;
|
||||
std::unique_ptr<QMenu> m_pTrayIconMenu;
|
||||
std::function<void()> m_init;
|
||||
std::function<void(const QIcon &icon)> m_setIcon;
|
||||
QIcon m_icon;
|
||||
};
|
||||
|
||||
} // namespace synergy::gui
|
||||
42
src/lib/gui/byte_utils.h
Normal file
42
src/lib/gui/byte_utils.h
Normal file
|
|
@ -0,0 +1,42 @@
|
|||
/*
|
||||
* 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 <QByteArray>
|
||||
#include <QDataStream>
|
||||
#include <QIODevice>
|
||||
#include <qlogging.h>
|
||||
|
||||
namespace synergy::gui {
|
||||
|
||||
inline int bytesToInt(const char *buffer, size_t size) {
|
||||
QByteArray byteArray(buffer, static_cast<int>(size));
|
||||
QDataStream stream(byteArray);
|
||||
int result;
|
||||
stream >> result;
|
||||
return result;
|
||||
}
|
||||
|
||||
inline QByteArray intToBytes(int value) {
|
||||
QByteArray bytes;
|
||||
QDataStream stream(&bytes, QIODevice::WriteOnly);
|
||||
stream << value;
|
||||
return bytes;
|
||||
}
|
||||
|
||||
} // namespace synergy::gui
|
||||
|
|
@ -28,13 +28,15 @@
|
|||
#include <QtNetwork>
|
||||
#include <functional>
|
||||
|
||||
using ConfigScopes = synergy::gui::ConfigScopes;
|
||||
using IConfigScopes = synergy::gui::IConfigScopes;
|
||||
using namespace synergy::gui;
|
||||
|
||||
// this should be incremented each time the wizard is changed,
|
||||
// which will force it to re-run for existing installations.
|
||||
const int kWizardVersion = 8;
|
||||
|
||||
static const char *const kLogLevelNames[] = {
|
||||
"INFO", "DEBUG", "DEBUG1", "DEBUG2"};
|
||||
|
||||
#if defined(Q_OS_WIN)
|
||||
const char AppConfig::m_CoreServerName[] = "synergys.exe";
|
||||
const char AppConfig::m_CoreClientName[] = "synergyc.exe";
|
||||
|
|
@ -94,11 +96,9 @@ const char *const AppConfig::m_SettingsName[] = {
|
|||
"showCloseReminder",
|
||||
};
|
||||
|
||||
static const char *logLevelNames[] = {"INFO", "DEBUG", "DEBUG1", "DEBUG2"};
|
||||
|
||||
AppConfig::AppConfig(
|
||||
synergy::gui::IConfigScopes &scopes, std::shared_ptr<Deps> deps)
|
||||
: m_scopes(scopes),
|
||||
: m_Scopes(scopes),
|
||||
m_pDeps(deps),
|
||||
m_ScreenName(deps->hostname()) {
|
||||
determineScope();
|
||||
|
|
@ -120,8 +120,8 @@ void AppConfig::recallFromAllScopes() {
|
|||
m_WizardLastRun = findInAllScopes(kWizardLastRun, m_WizardLastRun).toInt();
|
||||
m_LoadFromSystemScope =
|
||||
findInAllScopes(kLoadSystemSettings, m_LoadFromSystemScope).toBool();
|
||||
m_licenseNextCheck =
|
||||
findInAllScopes(kLicenseNextCheck, m_licenseNextCheck).toULongLong();
|
||||
m_LicenseNextCheck =
|
||||
findInAllScopes(kLicenseNextCheck, m_LicenseNextCheck).toULongLong();
|
||||
}
|
||||
|
||||
void AppConfig::recallFromCurrentScope() {
|
||||
|
|
@ -165,8 +165,7 @@ void AppConfig::recallFromCurrentScope() {
|
|||
m_CloseToTray = getFromCurrentScope(kCloseToTray, m_CloseToTray).toBool();
|
||||
m_TlsEnabled = getFromCurrentScope(kTlsEnabled, m_TlsEnabled).toBool();
|
||||
m_TlsCertPath = getFromCurrentScope(kTlsCertPath, m_TlsCertPath).toString();
|
||||
m_TlsKeyLength =
|
||||
getFromCurrentScope(kTlsKeyLength, m_TlsKeyLength).toString();
|
||||
m_TlsKeyLength = getFromCurrentScope(kTlsKeyLength, m_TlsKeyLength).toInt();
|
||||
m_MainWindowPosition = getFromCurrentScope<QPoint>(
|
||||
kMainWindowPosition, [](const QVariant &v) { return v.toPoint(); });
|
||||
m_MainWindowSize = getFromCurrentScope<QSize>(
|
||||
|
|
@ -197,13 +196,13 @@ void AppConfig::recallScreenName() {
|
|||
void AppConfig::commit() {
|
||||
using enum Setting;
|
||||
|
||||
qDebug("comitting app config");
|
||||
qDebug("committing app config");
|
||||
|
||||
saveToAllScopes(kWizardLastRun, m_WizardLastRun);
|
||||
saveToAllScopes(kLoadSystemSettings, m_LoadFromSystemScope);
|
||||
saveToAllScopes(kClientGroupChecked, m_ClientGroupChecked);
|
||||
saveToAllScopes(kServerGroupChecked, m_ServerGroupChecked);
|
||||
saveToAllScopes(kLicenseNextCheck, m_licenseNextCheck);
|
||||
saveToAllScopes(kLicenseNextCheck, m_LicenseNextCheck);
|
||||
|
||||
if (isActiveScopeWritable()) {
|
||||
setInCurrentScope(kScreenName, m_ScreenName);
|
||||
|
|
@ -214,7 +213,8 @@ void AppConfig::commit() {
|
|||
setInCurrentScope(kLogFilename, m_LogFilename);
|
||||
setInCurrentScope(kStartedBefore, m_StartedBefore);
|
||||
setInCurrentScope(kElevateMode, static_cast<int>(m_ElevateMode));
|
||||
setInCurrentScope(kElevateModeLegacy, m_ElevateMode == ElevateAlways);
|
||||
setInCurrentScope(
|
||||
kElevateModeLegacy, m_ElevateMode == ElevateMode::kAlways);
|
||||
setInCurrentScope(kTlsEnabled, m_TlsEnabled);
|
||||
setInCurrentScope(kAutoHide, m_AutoHide);
|
||||
setInCurrentScope(kSerialKey, m_SerialKey);
|
||||
|
|
@ -249,11 +249,11 @@ void AppConfig::determineScope() {
|
|||
|
||||
// first, try to determine if the system scope should be used according to the
|
||||
// user scope...
|
||||
if (m_scopes.scopeContains(
|
||||
if (m_Scopes.scopeContains(
|
||||
settingName(Setting::kLoadSystemSettings),
|
||||
ConfigScopes::Scope::User)) {
|
||||
auto loadFromSystemScope =
|
||||
m_scopes
|
||||
m_Scopes
|
||||
.getFromScope(
|
||||
settingName(Setting::kLoadSystemSettings),
|
||||
m_LoadFromSystemScope, ConfigScopes::Scope::User)
|
||||
|
|
@ -265,7 +265,7 @@ void AppConfig::determineScope() {
|
|||
// ...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(
|
||||
else if (m_Scopes.scopeContains(
|
||||
settingName(Setting::kScreenName),
|
||||
ConfigScopes::Scope::System)) {
|
||||
setLoadFromSystemScope(true);
|
||||
|
|
@ -275,7 +275,7 @@ void AppConfig::determineScope() {
|
|||
void AppConfig::recallSerialKey() {
|
||||
using enum Setting;
|
||||
|
||||
if (!m_scopes.scopeContains(settingName(kLoadSystemSettings))) {
|
||||
if (!m_Scopes.scopeContains(settingName(kLoadSystemSettings))) {
|
||||
qDebug("no serial key in current scope, skipping");
|
||||
return;
|
||||
}
|
||||
|
|
@ -294,7 +294,7 @@ void AppConfig::recallSerialKey() {
|
|||
void AppConfig::recallElevateMode() {
|
||||
using enum Setting;
|
||||
|
||||
if (!m_scopes.scopeContains(settingName(kElevateMode))) {
|
||||
if (!m_Scopes.scopeContains(settingName(kElevateMode))) {
|
||||
qDebug("elevate mode not set yet, skipping");
|
||||
return;
|
||||
}
|
||||
|
|
@ -322,24 +322,24 @@ QString AppConfig::settingName(Setting name) {
|
|||
}
|
||||
|
||||
template <typename T> void AppConfig::setInCurrentScope(Setting name, T value) {
|
||||
m_scopes.setInScope(settingName(name), value);
|
||||
m_Scopes.setInScope(settingName(name), value);
|
||||
}
|
||||
|
||||
template <typename T> void AppConfig::saveToAllScopes(Setting name, T value) {
|
||||
m_scopes.setInScope(settingName(name), value, ConfigScopes::Scope::User);
|
||||
m_scopes.setInScope(settingName(name), value, ConfigScopes::Scope::System);
|
||||
m_Scopes.setInScope(settingName(name), value, ConfigScopes::Scope::User);
|
||||
m_Scopes.setInScope(settingName(name), value, ConfigScopes::Scope::System);
|
||||
}
|
||||
|
||||
QVariant AppConfig::getFromCurrentScope(
|
||||
Setting name, const QVariant &defaultValue) const {
|
||||
return m_scopes.getFromScope(settingName(name), defaultValue);
|
||||
return m_Scopes.getFromScope(settingName(name), defaultValue);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
std::optional<T> AppConfig::getFromCurrentScope(
|
||||
Setting name, std::function<T(const QVariant &)> toType) const {
|
||||
if (m_scopes.scopeContains(settingName(name))) {
|
||||
return toType(m_scopes.getFromScope(settingName(name)));
|
||||
if (m_Scopes.scopeContains(settingName(name))) {
|
||||
return toType(m_Scopes.getFromScope(settingName(name)));
|
||||
} else {
|
||||
return std::nullopt;
|
||||
}
|
||||
|
|
@ -348,7 +348,7 @@ std::optional<T> AppConfig::getFromCurrentScope(
|
|||
template <typename T>
|
||||
void AppConfig::setInCurrentScope(Setting name, const std::optional<T> &value) {
|
||||
if (value.has_value()) {
|
||||
m_scopes.setInScope(settingName(name), value.value());
|
||||
m_Scopes.setInScope(settingName(name), value.value());
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -359,14 +359,14 @@ AppConfig::findInAllScopes(Setting name, const QVariant &defaultValue) const {
|
|||
QVariant result(defaultValue);
|
||||
QString setting(settingName(name));
|
||||
|
||||
if (m_scopes.scopeContains(setting)) {
|
||||
result = m_scopes.getFromScope(setting, defaultValue);
|
||||
} else if (m_scopes.activeScope() == System) {
|
||||
if (m_scopes.scopeContains(setting, User)) {
|
||||
result = m_scopes.getFromScope(setting, defaultValue, User);
|
||||
if (m_Scopes.scopeContains(setting)) {
|
||||
result = m_Scopes.getFromScope(setting, defaultValue);
|
||||
} else if (m_Scopes.activeScope() == System) {
|
||||
if (m_Scopes.scopeContains(setting, User)) {
|
||||
result = m_Scopes.getFromScope(setting, defaultValue, User);
|
||||
}
|
||||
} else if (m_scopes.scopeContains(setting, System)) {
|
||||
result = m_scopes.getFromScope(setting, defaultValue, System);
|
||||
} else if (m_Scopes.scopeContains(setting, System)) {
|
||||
result = m_Scopes.getFromScope(setting, defaultValue, System);
|
||||
}
|
||||
|
||||
return result;
|
||||
|
|
@ -388,18 +388,18 @@ void AppConfig::loadScope(ConfigScopes::Scope scope) {
|
|||
qFatal("invalid scope");
|
||||
}
|
||||
|
||||
if (m_scopes.activeScope() == scope) {
|
||||
if (m_Scopes.activeScope() == scope) {
|
||||
qDebug("already in required scope, skipping");
|
||||
return;
|
||||
}
|
||||
|
||||
m_scopes.setActiveScope(scope);
|
||||
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();
|
||||
if (m_Scopes.scopeContains(
|
||||
settingName(Setting::kScreenName), m_Scopes.activeScope())) {
|
||||
m_Scopes.signalReady();
|
||||
} else {
|
||||
qDebug("no screen name in scope, skipping");
|
||||
}
|
||||
|
|
@ -421,11 +421,11 @@ void AppConfig::setLoadFromSystemScope(bool value) {
|
|||
}
|
||||
|
||||
bool AppConfig::isActiveScopeWritable() const {
|
||||
return m_scopes.isActiveScopeWritable();
|
||||
return m_Scopes.isActiveScopeWritable();
|
||||
}
|
||||
|
||||
bool AppConfig::isActiveScopeSystem() const {
|
||||
return m_scopes.activeScope() == ConfigScopes::Scope::System;
|
||||
return m_Scopes.activeScope() == ConfigScopes::Scope::System;
|
||||
}
|
||||
|
||||
QString AppConfig::logDir() const {
|
||||
|
|
@ -446,7 +446,7 @@ void AppConfig::persistLogDir() const {
|
|||
// Begin getters
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
IConfigScopes &AppConfig::scopes() { return m_scopes; }
|
||||
IConfigScopes &AppConfig::scopes() { return m_Scopes; }
|
||||
|
||||
bool AppConfig::activationHasRun() const { return m_ActivationHasRun; }
|
||||
|
||||
|
|
@ -464,7 +464,7 @@ bool AppConfig::logToFile() const { return m_LogToFile; }
|
|||
|
||||
const QString &AppConfig::logFilename() const { return m_LogFilename; }
|
||||
|
||||
QString AppConfig::logLevelText() const { return logLevelNames[logLevel()]; }
|
||||
QString AppConfig::logLevelText() const { return kLogLevelNames[logLevel()]; }
|
||||
|
||||
ProcessMode AppConfig::processMode() const {
|
||||
return m_EnableService ? ProcessMode::kService : ProcessMode::kDesktop;
|
||||
|
|
@ -493,7 +493,7 @@ bool AppConfig::invertScrollDirection() const {
|
|||
}
|
||||
|
||||
unsigned long long AppConfig::licenseNextCheck() const {
|
||||
return m_licenseNextCheck;
|
||||
return m_LicenseNextCheck;
|
||||
}
|
||||
|
||||
bool AppConfig::languageSync() const { return m_LanguageSync; }
|
||||
|
|
@ -504,7 +504,7 @@ bool AppConfig::invertConnection() const { return m_InvertConnection; }
|
|||
|
||||
QString AppConfig::tlsCertPath() const { return m_TlsCertPath; }
|
||||
|
||||
QString AppConfig::tlsKeyLength() const { return m_TlsKeyLength; }
|
||||
int AppConfig::tlsKeyLength() const { return m_TlsKeyLength; }
|
||||
|
||||
bool AppConfig::enableService() const { return m_EnableService; }
|
||||
|
||||
|
|
@ -555,18 +555,28 @@ void AppConfig::setTlsEnabled(bool value) {
|
|||
}
|
||||
|
||||
void AppConfig::setTlsCertPath(const QString &value) {
|
||||
m_TlsChanged = m_TlsCertPath != value;
|
||||
if (m_TlsCertPath != value) {
|
||||
// deliberately only set the changed flag if there was a change.
|
||||
// it's important not to set this flag to false here.
|
||||
m_TlsChanged = true;
|
||||
}
|
||||
m_TlsCertPath = value;
|
||||
}
|
||||
|
||||
void AppConfig::setTlsKeyLength(const QString &value) {
|
||||
m_TlsChanged = m_TlsKeyLength != value;
|
||||
void AppConfig::setTlsKeyLength(int value) {
|
||||
if (m_TlsKeyLength != value) {
|
||||
// deliberately only set the changed flag if there was a change.
|
||||
// it's important not to set this flag to false here.
|
||||
m_TlsChanged = true;
|
||||
}
|
||||
m_TlsKeyLength = value;
|
||||
}
|
||||
|
||||
void AppConfig::setSerialKey(const QString &serialKey) {
|
||||
m_SerialKey = serialKey;
|
||||
saveToAllScopes(Setting::kSerialKey, m_SerialKey);
|
||||
}
|
||||
|
||||
void AppConfig::setServerGroupChecked(bool newValue) {
|
||||
m_ServerGroupChecked = newValue;
|
||||
}
|
||||
|
|
@ -618,7 +628,7 @@ void AppConfig::setElevateMode(ElevateMode em) { m_ElevateMode = em; }
|
|||
void AppConfig::setAutoHide(bool b) { m_AutoHide = b; }
|
||||
|
||||
void AppConfig::setLicenseNextCheck(unsigned long long time) {
|
||||
m_licenseNextCheck = time;
|
||||
m_LicenseNextCheck = time;
|
||||
}
|
||||
|
||||
void AppConfig::setInvertScrollDirection(bool newValue) {
|
||||
|
|
@ -18,10 +18,10 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include "ConfigScopes.h"
|
||||
#include "CoreInterface.h"
|
||||
#include "ElevateMode.h"
|
||||
#include "IAppConfig.h"
|
||||
#include "IConfigScopes.h"
|
||||
#include "gui/core/CoreTool.h"
|
||||
|
||||
#include <QDir>
|
||||
#include <QHostInfo>
|
||||
|
|
@ -32,10 +32,11 @@
|
|||
#include <QVariant>
|
||||
#include <optional>
|
||||
|
||||
enum class ProcessMode { kService, kDesktop };
|
||||
namespace synergy::gui {
|
||||
|
||||
const ElevateMode kDefaultElevateMode = ElevateAsNeeded;
|
||||
const ElevateMode kDefaultElevateMode = ElevateMode::kAutomatic;
|
||||
const QString kDefaultLogFile = "synergy.log";
|
||||
const int kDefaultTlsKeyLength = 2048;
|
||||
|
||||
#if defined(Q_OS_WIN)
|
||||
const ProcessMode kDefaultProcessMode = ProcessMode::kService;
|
||||
|
|
@ -49,6 +50,8 @@ const bool kDefaultShowDevThanks = true;
|
|||
const bool kDefaultShowDevThanks = false;
|
||||
#endif // SYNERGY_SHOW_DEV_THANKS
|
||||
|
||||
} // namespace synergy::gui
|
||||
|
||||
/**
|
||||
* @brief Simply reads and writes app settings.
|
||||
*
|
||||
|
|
@ -58,6 +61,9 @@ const bool kDefaultShowDevThanks = false;
|
|||
* becoming a god object.
|
||||
*/
|
||||
class AppConfig : public QObject, public synergy::gui::IAppConfig {
|
||||
using ProcessMode = synergy::gui::ProcessMode;
|
||||
using IConfigScopes = synergy::gui::IConfigScopes;
|
||||
|
||||
Q_OBJECT
|
||||
|
||||
private:
|
||||
|
|
@ -110,20 +116,19 @@ private:
|
|||
public:
|
||||
struct Deps {
|
||||
virtual ~Deps() = default;
|
||||
virtual QString profileDir() const {
|
||||
return m_coreInterface.getProfileDir();
|
||||
}
|
||||
virtual QString profileDir() const { return m_coreTool.getProfileDir(); }
|
||||
virtual QString hostname() const { return QHostInfo::localHostName(); }
|
||||
|
||||
private:
|
||||
[[no_unique_address]] CoreInterface m_coreInterface;
|
||||
[[no_unique_address]] CoreTool m_coreTool;
|
||||
};
|
||||
|
||||
explicit AppConfig(
|
||||
synergy::gui::IConfigScopes &scopes,
|
||||
IConfigScopes &scopes,
|
||||
std::shared_ptr<Deps> deps = std::make_shared<Deps>());
|
||||
|
||||
synergy::gui::IConfigScopes &scopes();
|
||||
IConfigScopes &scopes();
|
||||
void determineScope();
|
||||
|
||||
/**
|
||||
* @brief Commits the current settings to the active scope.
|
||||
|
|
@ -131,50 +136,47 @@ public:
|
|||
*/
|
||||
void commit();
|
||||
|
||||
void determineScope();
|
||||
|
||||
/**
|
||||
* Getters
|
||||
*/
|
||||
|
||||
void setActivationHasRun(bool value);
|
||||
ProcessMode processMode() const override;
|
||||
ElevateMode elevateMode() const override;
|
||||
bool tlsEnabled() const override;
|
||||
QString tlsCertPath() const override;
|
||||
int tlsKeyLength() const override;
|
||||
QString logLevelText() const override;
|
||||
const QString &screenName() const override;
|
||||
bool logToFile() const override;
|
||||
bool preventSleep() const override;
|
||||
const QString &logFilename() const override;
|
||||
QString coreServerName() const override;
|
||||
QString coreClientName() const override;
|
||||
bool invertConnection() const override;
|
||||
void persistLogDir() const override;
|
||||
QString serialKey() const override;
|
||||
bool languageSync() const override;
|
||||
bool invertScrollDirection() const override;
|
||||
int port() const override;
|
||||
bool useExternalConfig() const override;
|
||||
const QString &configFile() const override;
|
||||
const QString &networkInterface() const override;
|
||||
bool isActiveScopeWritable() const;
|
||||
bool isActiveScopeSystem() const;
|
||||
const QString &screenName() const;
|
||||
int port() const;
|
||||
const QString &networkInterface() const;
|
||||
int logLevel() const;
|
||||
bool logToFile() const;
|
||||
const QString &logFilename() const;
|
||||
QString logLevelText() const;
|
||||
ProcessMode processMode() const;
|
||||
bool wizardShouldRun() const;
|
||||
bool startedBefore() const;
|
||||
QString coreServerName() const;
|
||||
QString coreClientName() const;
|
||||
QString logDir() const;
|
||||
void persistLogDir() const;
|
||||
ElevateMode elevateMode() const;
|
||||
bool autoHide() const;
|
||||
bool invertScrollDirection() const;
|
||||
unsigned long long licenseNextCheck() const;
|
||||
bool languageSync() const;
|
||||
bool preventSleep() const;
|
||||
bool invertConnection() const;
|
||||
bool serverGroupChecked() const;
|
||||
bool useExternalConfig() const;
|
||||
const QString &configFile() const;
|
||||
bool useInternalConfig() const;
|
||||
bool clientGroupChecked() const;
|
||||
QString serverHostname() const;
|
||||
QString lastVersion() const;
|
||||
bool enableService() const;
|
||||
bool closeToTray() const;
|
||||
QString serialKey() const;
|
||||
bool activationHasRun() const;
|
||||
bool tlsEnabled() const override;
|
||||
QString tlsCertPath() const override;
|
||||
QString tlsKeyLength() const override;
|
||||
std::optional<QSize> mainWindowSize() const;
|
||||
std::optional<QPoint> mainWindowPosition() const;
|
||||
bool showDevThanks() const;
|
||||
|
|
@ -184,6 +186,7 @@ public:
|
|||
* Setters
|
||||
*/
|
||||
|
||||
void setActivationHasRun(bool value);
|
||||
void setScreenName(const QString &s);
|
||||
void setPort(int i);
|
||||
void setNetworkInterface(const QString &s);
|
||||
|
|
@ -211,7 +214,7 @@ public:
|
|||
void setEnableService(bool enabled);
|
||||
void setCloseToTray(bool minimize);
|
||||
void setTlsCertPath(const QString &path);
|
||||
void setTlsKeyLength(const QString &length);
|
||||
void setTlsKeyLength(int length);
|
||||
void setInvertConnection(bool value);
|
||||
void setMainWindowSize(const QSize &size);
|
||||
void setMainWindowPosition(const QPoint &position);
|
||||
|
|
@ -274,30 +277,30 @@ private:
|
|||
|
||||
/// @brief This method loads config from specified scope
|
||||
/// @param [in] scope which should be loaded.
|
||||
void loadScope(synergy::gui::ConfigScopes::Scope scope);
|
||||
void loadScope(IConfigScopes::Scope scope);
|
||||
|
||||
/**
|
||||
* @brief Gets a TLS certificate path based on the user's profile dir.
|
||||
*/
|
||||
QString defaultTlsCertPath() const;
|
||||
|
||||
synergy::gui::IConfigScopes &m_scopes;
|
||||
synergy::gui::IConfigScopes &m_Scopes;
|
||||
std::shared_ptr<Deps> m_pDeps;
|
||||
QString m_ScreenName;
|
||||
int m_Port = 24800;
|
||||
QString m_Interface = "";
|
||||
int m_LogLevel = 0;
|
||||
bool m_LogToFile = false;
|
||||
QString m_LogFilename = logDir() + kDefaultLogFile;
|
||||
QString m_LogFilename = logDir() + synergy::gui::kDefaultLogFile;
|
||||
int m_WizardLastRun = 0;
|
||||
bool m_StartedBefore = false;
|
||||
ElevateMode m_ElevateMode = kDefaultElevateMode;
|
||||
ElevateMode m_ElevateMode = synergy::gui::kDefaultElevateMode;
|
||||
QString m_ActivateEmail = "";
|
||||
bool m_TlsEnabled = true;
|
||||
bool m_AutoHide = false;
|
||||
QString m_SerialKey = "";
|
||||
QString m_LastVersion = "";
|
||||
unsigned long long m_licenseNextCheck = 0;
|
||||
unsigned long long m_LicenseNextCheck = 0;
|
||||
bool m_ActivationHasRun = false;
|
||||
bool m_InvertScrollDirection = false;
|
||||
bool m_LanguageSync = true;
|
||||
|
|
@ -309,13 +312,14 @@ private:
|
|||
bool m_UseInternalConfig = false;
|
||||
bool m_ClientGroupChecked = false;
|
||||
QString m_ServerHostname = "";
|
||||
bool m_EnableService = kDefaultProcessMode == ProcessMode::kService;
|
||||
bool m_EnableService =
|
||||
synergy::gui::kDefaultProcessMode == ProcessMode::kService;
|
||||
bool m_CloseToTray = true;
|
||||
QString m_TlsCertPath = defaultTlsCertPath();
|
||||
QString m_TlsKeyLength = "2048";
|
||||
int m_TlsKeyLength = synergy::gui::kDefaultTlsKeyLength;
|
||||
std::optional<QSize> m_MainWindowSize;
|
||||
std::optional<QPoint> m_MainWindowPosition;
|
||||
bool m_ShowDevThanks = kDefaultShowDevThanks;
|
||||
bool m_ShowDevThanks = synergy::gui::kDefaultShowDevThanks;
|
||||
bool m_LoadFromSystemScope = false;
|
||||
bool m_ShowCloseReminder = true;
|
||||
|
||||
|
|
@ -18,20 +18,23 @@
|
|||
#include "ConfigScopes.h"
|
||||
|
||||
#include <QCoreApplication>
|
||||
#include <QDir>
|
||||
#include <QFile>
|
||||
#include <cassert>
|
||||
#include <memory>
|
||||
|
||||
const auto kSystemConfigFilename = "SystemConfig.ini";
|
||||
|
||||
#if defined(Q_OS_UNIX)
|
||||
const auto kUnixSystemConfigPath = "/usr/local/etc/symless/";
|
||||
#endif
|
||||
|
||||
namespace synergy::gui {
|
||||
|
||||
QString getSystemSettingPath() {
|
||||
const QString settingFilename(kSystemConfigFilename);
|
||||
#if defined(Q_OS_WIN)
|
||||
return QCoreApplication::applicationDirPath() + "\\";
|
||||
#elif defined(Q_OS_DARWIN)
|
||||
return QCoreApplication::applicationDirPath() + QDir::separator();
|
||||
#elif defined(Q_OS_MAC)
|
||||
// it would be nice to use /Library dir, but qt has no elevate system.
|
||||
return kUnixSystemConfigPath + settingFilename;
|
||||
#elif defined(Q_OS_LINUX)
|
||||
|
|
@ -32,7 +32,7 @@ class ConfigScopes : public QObject, public IConfigScopes {
|
|||
|
||||
public:
|
||||
explicit ConfigScopes();
|
||||
virtual ~ConfigScopes() = default;
|
||||
~ConfigScopes() override = default;
|
||||
|
||||
void signalReady() override;
|
||||
void save() override;
|
||||
38
src/lib/gui/config/ElevateMode.h
Normal file
38
src/lib/gui/config/ElevateMode.h
Normal file
|
|
@ -0,0 +1,38 @@
|
|||
/*
|
||||
* synergy -- mouse and keyboard sharing utility
|
||||
* Copyright (C) 2016 Symless
|
||||
*
|
||||
* 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
|
||||
|
||||
/**
|
||||
* @brief The elevate mode tristate determines two behaviors on Windows.
|
||||
*
|
||||
* The first, switch-on-desk-switch (SodS), passed through synergyd as a
|
||||
* command line argument to synergy core, determines if the server restarts
|
||||
* when switching Windows desktops (e.g. when Windows UAC dialog pops up).
|
||||
* The second, passed as a boolean flag to Synergyd over the IPC inside
|
||||
* IpcMessageType::CommandMessage, determines whether Synergy should be started
|
||||
* with elevated privileges.
|
||||
*
|
||||
* The matrix for these two behaviors is as follows:
|
||||
*
|
||||
* | SodS | Elevate |
|
||||
* |-----------|------------|
|
||||
* kAutomatic | true | false |
|
||||
* kAlways | false | true |
|
||||
* kNever | false | false |
|
||||
*/
|
||||
enum class ElevateMode { kAutomatic = 0, kAlways = 1, kNever = 2 };
|
||||
54
src/lib/gui/config/IAppConfig.h
Normal file
54
src/lib/gui/config/IAppConfig.h
Normal file
|
|
@ -0,0 +1,54 @@
|
|||
/*
|
||||
* 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 "ElevateMode.h"
|
||||
|
||||
#include <QString>
|
||||
|
||||
namespace synergy::gui {
|
||||
|
||||
enum class ProcessMode { kService, kDesktop };
|
||||
|
||||
class IAppConfig {
|
||||
public:
|
||||
virtual ~IAppConfig() = default;
|
||||
virtual QString tlsCertPath() const = 0;
|
||||
virtual int tlsKeyLength() const = 0;
|
||||
virtual bool tlsEnabled() const = 0;
|
||||
virtual ProcessMode processMode() const = 0;
|
||||
virtual ElevateMode elevateMode() const = 0;
|
||||
virtual QString logLevelText() const = 0;
|
||||
virtual const QString &screenName() const = 0;
|
||||
virtual bool preventSleep() const = 0;
|
||||
virtual bool logToFile() const = 0;
|
||||
virtual const QString &logFilename() const = 0;
|
||||
virtual QString coreServerName() const = 0;
|
||||
virtual QString coreClientName() const = 0;
|
||||
virtual bool invertConnection() const = 0;
|
||||
virtual void persistLogDir() const = 0;
|
||||
virtual QString serialKey() const = 0;
|
||||
virtual bool languageSync() const = 0;
|
||||
virtual bool invertScrollDirection() const = 0;
|
||||
virtual int port() const = 0;
|
||||
virtual bool useExternalConfig() const = 0;
|
||||
virtual const QString &configFile() const = 0;
|
||||
virtual const QString &networkInterface() const = 0;
|
||||
};
|
||||
|
||||
} // namespace synergy::gui
|
||||
35
src/lib/gui/config/IServerConfig.h
Normal file
35
src/lib/gui/config/IServerConfig.h
Normal file
|
|
@ -0,0 +1,35 @@
|
|||
/*
|
||||
* 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 <QFile>
|
||||
#include <QString>
|
||||
|
||||
namespace synergy::gui {
|
||||
|
||||
class IServerConfig {
|
||||
public:
|
||||
virtual ~IServerConfig() = default;
|
||||
virtual bool isFull() const = 0;
|
||||
virtual bool screenExists(const QString &screenName) const = 0;
|
||||
virtual bool save(const QString &fileName) const = 0;
|
||||
virtual void save(QFile &file) const = 0;
|
||||
virtual bool enableDragAndDrop() const = 0;
|
||||
};
|
||||
|
||||
} // namespace synergy::gui
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
/*
|
||||
* synergy -- mouse and keyboard sharing utility
|
||||
* Copyright (C) 2014 Symless Ltd.
|
||||
* Copyright (C) 2024 Symless Ltd.
|
||||
*
|
||||
* This package is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
|
|
@ -19,23 +19,36 @@
|
|||
|
||||
#include <QString>
|
||||
|
||||
namespace synergy::gui {
|
||||
|
||||
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";
|
||||
|
||||
const int kDebugLogLevel = 1;
|
||||
|
||||
#ifdef SYNERGY_PRODUCT_NAME
|
||||
const QString kProductName = SYNERGY_PRODUCT_NAME;
|
||||
#else
|
||||
const QString kProductName;
|
||||
#endif
|
||||
|
||||
#ifdef SYNERGY_ENABLE_LICENSING
|
||||
const bool kLicensingEnabled = true;
|
||||
#ifdef SYNERGY_LICENSED_PRODUCT
|
||||
const bool kLicensedProduct = true;
|
||||
#else
|
||||
const bool kLicensingEnabled = false;
|
||||
#endif // SYNERGY_ENABLE_LICENSING
|
||||
const bool kLicensedProduct = false;
|
||||
#endif
|
||||
|
||||
#ifdef SYNERGY_ENABLE_ACTIVATION
|
||||
#ifndef SYNERGY_LICENSED_PRODUCT
|
||||
#error "activation requires licensed product"
|
||||
#endif
|
||||
const bool kEnableActivation = true;
|
||||
#else
|
||||
const bool kEnableActivation = false;
|
||||
#endif // SYNERGY_ENABLE_ACTIVATION
|
||||
|
||||
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>)";
|
||||
|
|
@ -43,7 +56,7 @@ const auto kLinkDownload = R"(<a href="%1" style="color: %2">Download now</a>)";
|
|||
|
||||
const auto kUrlSourceQuery = "source=gui";
|
||||
const auto kUrlWebsite = "https://symless.com";
|
||||
const auto kUrlContribute = "https://github.com/symless/synergy-core";
|
||||
const auto kUrlGitHub = "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);
|
||||
|
|
@ -54,3 +67,6 @@ const auto kUrlContact =
|
|||
const auto kUrlHelp = QString("%1/help?%2").arg(kUrlProduct, kUrlSourceQuery);
|
||||
const auto kUrlDownload =
|
||||
QString("%1/download?%2").arg(kUrlProduct, kUrlSourceQuery);
|
||||
const auto kUrlBugReport = QString("%1/issues").arg(kUrlGitHub);
|
||||
|
||||
} // namespace synergy::gui
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
/*
|
||||
* synergy -- mouse and keyboard sharing utility
|
||||
* Copyright (C) 2012-2021 Symless Ltd.
|
||||
* Copyright (C) 2008 Volker Lanz (vl@fidra.de)
|
||||
* Copyright (C) 2021 Symless Ltd.
|
||||
*
|
||||
* This package is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
|
|
@ -18,12 +17,14 @@
|
|||
|
||||
#include "ClientConnection.h"
|
||||
|
||||
#include "MainWindow.h"
|
||||
|
||||
#include <QHostAddress>
|
||||
#include <QMessageBox>
|
||||
|
||||
ClientConnection::ClientConnection(MainWindow &parent) : m_parent(parent) {}
|
||||
namespace synergy::gui {
|
||||
|
||||
ClientConnection::ClientConnection(QWidget &parent, AppConfig &appConfig)
|
||||
: m_parent(parent),
|
||||
m_appConfig(appConfig) {}
|
||||
|
||||
void ClientConnection::update(const QString &line) {
|
||||
if (m_checkConnection && checkMainWindow()) {
|
||||
|
|
@ -59,13 +60,13 @@ QString ClientConnection::getMessage(const QString &line) const {
|
|||
message =
|
||||
QObject::tr("Connection failed.\nYou can’t name 2 computers the same.");
|
||||
} else {
|
||||
QHostAddress address(m_parent.appConfig().serverHostname());
|
||||
QHostAddress address(m_appConfig.serverHostname());
|
||||
if (address.isNull()) {
|
||||
message =
|
||||
QObject::tr(
|
||||
"We can’t connect to the server \"%1\" try to connect using the "
|
||||
"server IP address and check your firewall settings.")
|
||||
.arg(m_parent.appConfig().serverHostname());
|
||||
.arg(m_appConfig.serverHostname());
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -82,3 +83,5 @@ void ClientConnection::showMessage(const QString &message) const {
|
|||
void ClientConnection::setCheckConnection(bool checkConnection) {
|
||||
m_checkConnection = checkConnection;
|
||||
}
|
||||
|
||||
} // namespace synergy::gui
|
||||
|
|
@ -1,7 +1,6 @@
|
|||
/*
|
||||
* synergy -- mouse and keyboard sharing utility
|
||||
* Copyright (C) 2012-2021 Symless Ltd.
|
||||
* Copyright (C) 2008 Volker Lanz (vl@fidra.de)
|
||||
* Copyright (C) 2021 Symless Ltd.
|
||||
*
|
||||
* This package is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
|
|
@ -16,19 +15,18 @@
|
|||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef CLIENTCONNECTION_H
|
||||
#define CLIENTCONNECTION_H
|
||||
#pragma once
|
||||
|
||||
#include "gui/config/AppConfig.h"
|
||||
|
||||
#include <QString>
|
||||
#include <QWidget>
|
||||
|
||||
class MainWindow;
|
||||
namespace synergy::gui {
|
||||
|
||||
class ClientConnection {
|
||||
MainWindow &m_parent;
|
||||
bool m_checkConnection = false;
|
||||
|
||||
public:
|
||||
explicit ClientConnection(MainWindow &parent);
|
||||
explicit ClientConnection(QWidget &parent, AppConfig &appConfig);
|
||||
void update(const QString &line);
|
||||
void setCheckConnection(bool checkConnection);
|
||||
|
||||
|
|
@ -36,6 +34,10 @@ private:
|
|||
QString getMessage(const QString &line) const;
|
||||
bool checkMainWindow();
|
||||
void showMessage(const QString &message) const;
|
||||
|
||||
QWidget &m_parent;
|
||||
AppConfig &m_appConfig;
|
||||
bool m_checkConnection = false;
|
||||
};
|
||||
|
||||
#endif // CLIENTCONNECTION_H
|
||||
} // namespace synergy::gui
|
||||
|
|
@ -39,8 +39,7 @@ QString CommandProcess::run() {
|
|||
}
|
||||
|
||||
if (process.waitForFinished()) {
|
||||
output =
|
||||
QString::fromLocal8Bit(process.readAllStandardOutput().trimmed());
|
||||
output = process.readAllStandardOutput().trimmed();
|
||||
error = process.readAllStandardError().trimmed();
|
||||
}
|
||||
}
|
||||
696
src/lib/gui/core/CoreProcess.cpp
Normal file
696
src/lib/gui/core/CoreProcess.cpp
Normal file
|
|
@ -0,0 +1,696 @@
|
|||
/*
|
||||
* 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 "CoreProcess.h"
|
||||
|
||||
#include "constants.h"
|
||||
#include "gui/config/IAppConfig.h"
|
||||
#include "gui/core/CoreTool.h"
|
||||
#include <qglobal.h>
|
||||
#include <qmutex.h>
|
||||
|
||||
#if defined(Q_OS_MAC)
|
||||
#include "OSXHelpers.h"
|
||||
#endif
|
||||
|
||||
#include <QCoreApplication>
|
||||
#include <QDir>
|
||||
#include <QFile>
|
||||
#include <QMessageBox>
|
||||
#include <QMutexLocker>
|
||||
#include <QRegularExpression>
|
||||
#include <QStandardPaths>
|
||||
#include <QTimer>
|
||||
|
||||
namespace synergy::gui {
|
||||
|
||||
const int kRetryDelay = 1000;
|
||||
const auto kLastConfigFilename = "LastConfig.cfg";
|
||||
const auto kLineSplitRegex = QRegularExpression("\r|\n|\r\n");
|
||||
|
||||
//
|
||||
// free functions
|
||||
//
|
||||
|
||||
QString processModeToString(ProcessMode mode) {
|
||||
using enum ProcessMode;
|
||||
|
||||
switch (mode) {
|
||||
case kDesktop:
|
||||
return "desktop";
|
||||
case kService:
|
||||
return "service";
|
||||
default:
|
||||
qFatal("invalid process mode");
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Wraps options that contain spaces in quotes
|
||||
*
|
||||
* Useful to handle things like paths with spaces (e.g. "C:\Program Files").
|
||||
*
|
||||
* Can also be used to create a representation of a command that can be pasted
|
||||
* into a terminal.
|
||||
*/
|
||||
QString makeQuotedArgs(const QString &app, const QStringList &args) {
|
||||
QStringList command;
|
||||
command << app;
|
||||
command << args;
|
||||
|
||||
QStringList quoted;
|
||||
for (const auto &arg : command) {
|
||||
if (arg.contains(' ')) {
|
||||
quoted << QString("\"%1\"").arg(arg);
|
||||
} else {
|
||||
quoted << arg;
|
||||
}
|
||||
}
|
||||
|
||||
return quoted.join(" ");
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief If IPv6, ensures the IP is surround in square brackets.
|
||||
*/
|
||||
QString wrapIpv6(const QString &address) {
|
||||
if (!address.contains(':') || address.isEmpty()) {
|
||||
return address;
|
||||
}
|
||||
|
||||
QString wrapped = address;
|
||||
|
||||
if (address[0] != '[') {
|
||||
wrapped.insert(0, '[');
|
||||
}
|
||||
|
||||
if (address[address.size() - 1] != ']') {
|
||||
wrapped.push_back(']');
|
||||
}
|
||||
|
||||
return address;
|
||||
}
|
||||
|
||||
//
|
||||
// CoreProcess::Deps
|
||||
//
|
||||
|
||||
QString CoreProcess::Deps::appPath(const QString &name) const {
|
||||
QDir dir(QCoreApplication::applicationDirPath());
|
||||
return dir.filePath(name);
|
||||
}
|
||||
|
||||
bool CoreProcess::Deps::fileExists(const QString &path) const {
|
||||
return QFile::exists(path);
|
||||
}
|
||||
|
||||
QString CoreProcess::Deps::getProfileRoot() const {
|
||||
CoreTool coreTool;
|
||||
QDir appDir = coreTool.getProfileDir();
|
||||
|
||||
// the core expects the profile root dir, not the app-specific profile dir.
|
||||
if (!appDir.cdUp()) {
|
||||
qFatal("failed to cd up to profile root dir");
|
||||
}
|
||||
|
||||
return appDir.absolutePath();
|
||||
}
|
||||
|
||||
//
|
||||
// CoreProcess
|
||||
//
|
||||
|
||||
CoreProcess::CoreProcess(
|
||||
IAppConfig &appConfig, IServerConfig &serverConfig,
|
||||
std::shared_ptr<Deps> deps)
|
||||
: m_appConfig(appConfig),
|
||||
m_serverConfig(serverConfig),
|
||||
m_pDeps(deps) {
|
||||
|
||||
connect(
|
||||
&m_pDeps->ipcClient(), &QIpcClient::read, this, //
|
||||
&CoreProcess::onIpcClientRead);
|
||||
|
||||
connect(
|
||||
&m_pDeps->ipcClient(), &QIpcClient::serviceReady, this, //
|
||||
&CoreProcess::onIpcClientServiceReady);
|
||||
|
||||
connect(
|
||||
&m_pDeps->process(), &QProcessProxy::finished, this,
|
||||
&CoreProcess::onProcessFinished);
|
||||
|
||||
connect(
|
||||
&m_pDeps->process(), &QProcessProxy::readyReadStandardOutput, this,
|
||||
&CoreProcess::onProcessReadyReadStandardOutput);
|
||||
|
||||
connect(
|
||||
&m_pDeps->process(), &QProcessProxy::readyReadStandardError, this,
|
||||
&CoreProcess::onProcessReadyReadStandardError);
|
||||
}
|
||||
|
||||
void CoreProcess::onIpcClientServiceReady() {
|
||||
if (m_processState == ProcessState::Starting) {
|
||||
qDebug("service ready, continuing core process start");
|
||||
start();
|
||||
} else if (m_processState == ProcessState::Stopping) {
|
||||
qDebug("service ready, continuing core process stop");
|
||||
stop();
|
||||
} else {
|
||||
qCritical("service ready, but process state is not starting or stopping");
|
||||
}
|
||||
}
|
||||
|
||||
void CoreProcess::onIpcClientError(const QString &text) const {
|
||||
qCritical().noquote() << text;
|
||||
|
||||
if (m_appConfig.processMode() != ProcessMode::kService) {
|
||||
// if not meant to be in service mode and there is an ipc connection error,
|
||||
// then abandon the ipc client connection.
|
||||
m_pDeps->ipcClient().disconnectFromHost();
|
||||
}
|
||||
}
|
||||
|
||||
void CoreProcess::onIpcClientRead(const QString &text) { handleLogLines(text); }
|
||||
|
||||
void CoreProcess::onProcessReadyReadStandardOutput() {
|
||||
if (m_pDeps->process()) {
|
||||
handleLogLines(m_pDeps->process().readAllStandardOutput());
|
||||
}
|
||||
}
|
||||
|
||||
void CoreProcess::onProcessReadyReadStandardError() {
|
||||
if (m_pDeps->process()) {
|
||||
handleLogLines(m_pDeps->process().readAllStandardError());
|
||||
}
|
||||
}
|
||||
|
||||
void CoreProcess::onProcessFinished(int exitCode, QProcess::ExitStatus) {
|
||||
const auto wasStarted = m_processState == ProcessState::Started;
|
||||
|
||||
setProcessState(ProcessState::Stopped);
|
||||
setConnectionState(ConnectionState::Disconnected);
|
||||
|
||||
if (exitCode == 0) {
|
||||
qDebug("desktop process exited normally");
|
||||
} else {
|
||||
qWarning("desktop process exited with error code: %d", exitCode);
|
||||
}
|
||||
|
||||
if (wasStarted) {
|
||||
qDebug("desktop process was running, retrying in %d ms", kRetryDelay);
|
||||
QTimer::singleShot(kRetryDelay, [this] { start(); });
|
||||
}
|
||||
}
|
||||
|
||||
void CoreProcess::startDesktop(const QString &app, const QStringList &args) {
|
||||
using enum ProcessState;
|
||||
|
||||
if (m_processState != Starting) {
|
||||
qFatal("core process must be in starting state");
|
||||
}
|
||||
|
||||
// only make quoted args for printing the command for convenience; so that the
|
||||
// core command can be easily copy/pasted to the terminal for testing.
|
||||
const auto quoted = makeQuotedArgs(app, args);
|
||||
qInfo("running command: %s", qPrintable(quoted));
|
||||
|
||||
m_pDeps->process().start(app, args);
|
||||
|
||||
if (m_pDeps->process().waitForStarted()) {
|
||||
setProcessState(Started);
|
||||
} else {
|
||||
setProcessState(Stopped);
|
||||
emit error(Error::StartFailed);
|
||||
}
|
||||
}
|
||||
|
||||
void CoreProcess::startService(const QString &app, const QStringList &args) {
|
||||
using enum ProcessState;
|
||||
|
||||
if (m_processState != Starting) {
|
||||
qFatal("core process must be in starting state");
|
||||
}
|
||||
|
||||
if (!m_pDeps->ipcClient().isConnected()) {
|
||||
// when service state changes, start will be called again.
|
||||
qDebug("cannot start process, ipc not connected, connecting instead");
|
||||
m_pDeps->ipcClient().connectToHost();
|
||||
return;
|
||||
}
|
||||
|
||||
QString commandQuoted = makeQuotedArgs(app, args);
|
||||
|
||||
qInfo("running command: %s", qPrintable(commandQuoted));
|
||||
m_pDeps->ipcClient().sendCommand(commandQuoted, m_appConfig.elevateMode());
|
||||
setProcessState(Started);
|
||||
}
|
||||
|
||||
void CoreProcess::stopDesktop() const {
|
||||
if (m_processState != ProcessState::Stopping) {
|
||||
qFatal("core process must be in stopping state");
|
||||
}
|
||||
|
||||
if (!m_pDeps->process()) {
|
||||
qFatal("process not set, cannot stop");
|
||||
}
|
||||
|
||||
qInfo("stopping core desktop process");
|
||||
|
||||
if (m_pDeps->process().state() == QProcess::ProcessState::Running) {
|
||||
qDebug("process is running, closing");
|
||||
m_pDeps->process().close();
|
||||
} else {
|
||||
qDebug("process is not running, skipping terminate");
|
||||
}
|
||||
}
|
||||
|
||||
void CoreProcess::stopService() {
|
||||
if (m_processState != ProcessState::Stopping) {
|
||||
qFatal("core process must be in stopping state");
|
||||
}
|
||||
|
||||
if (!m_pDeps->ipcClient().isConnected()) {
|
||||
qDebug("cannot stop process, ipc not connected");
|
||||
return;
|
||||
}
|
||||
|
||||
m_pDeps->ipcClient().sendCommand("", m_appConfig.elevateMode());
|
||||
setProcessState(ProcessState::Stopped);
|
||||
}
|
||||
|
||||
void CoreProcess::handleLogLines(const QString &text) {
|
||||
for (const auto &line : text.split(kLineSplitRegex)) {
|
||||
if (line.isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
#if defined(Q_OS_MAC)
|
||||
// HACK: macOS 10.13.4+ spamming error lines in logs making them
|
||||
// impossible to read and debug; giving users a red herring.
|
||||
if (line.contains("calling TIS/TSM in non-main thread environment")) {
|
||||
continue;
|
||||
}
|
||||
#endif
|
||||
|
||||
checkLogLine(line);
|
||||
emit logLine(line);
|
||||
}
|
||||
}
|
||||
|
||||
void CoreProcess::start(std::optional<ProcessMode> processModeOption) {
|
||||
QMutexLocker locker(&m_processMutex);
|
||||
|
||||
const auto processMode =
|
||||
processModeOption.value_or(m_appConfig.processMode());
|
||||
|
||||
qInfo(
|
||||
"starting core %s process (%s mode)", qPrintable(modeString()),
|
||||
qPrintable(processModeToString(processMode)));
|
||||
|
||||
if (m_processState == ProcessState::Started) {
|
||||
qCritical("core process already started");
|
||||
return;
|
||||
}
|
||||
|
||||
// allow external listeners to abort the start process (e.g. licensing issue).
|
||||
setProcessState(ProcessState::Starting);
|
||||
emit starting();
|
||||
if (m_processState == ProcessState::Stopped) {
|
||||
qDebug("core process start was cancelled by listener");
|
||||
return;
|
||||
}
|
||||
|
||||
#ifdef Q_OS_MAC
|
||||
requestOSXNotificationPermission();
|
||||
#endif
|
||||
|
||||
setConnectionState(ConnectionState::Connecting);
|
||||
|
||||
QString app;
|
||||
QStringList args;
|
||||
|
||||
args << "-f"
|
||||
<< "--no-tray"
|
||||
<< "--debug" << m_appConfig.logLevelText();
|
||||
|
||||
args << "--name" << m_appConfig.screenName();
|
||||
|
||||
if (processMode == ProcessMode::kDesktop) {
|
||||
m_pDeps->process().create();
|
||||
} else {
|
||||
// tell client/server to talk to daemon through ipc.
|
||||
args << "--ipc";
|
||||
|
||||
#if defined(Q_OS_WIN)
|
||||
// tell the client/server to shut down when a ms windows desk
|
||||
// is switched; this is because we may need to elevate or not
|
||||
// based on which desk the user is in (login always needs
|
||||
// elevation, where as default desk does not).
|
||||
// Note that this is only enabled when synergy is set to elevate
|
||||
// 'as needed' (e.g. on a UAC dialog popup) in order to prevent
|
||||
// unnecessary restarts when synergy was started elevated or
|
||||
// when it is not allowed to elevate. In these cases restarting
|
||||
// the server is fruitless.
|
||||
if (m_appConfig.elevateMode() == ElevateMode::kAutomatic) {
|
||||
args << "--stop-on-desk-switch";
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifndef Q_OS_LINUX
|
||||
|
||||
if (m_serverConfig.enableDragAndDrop()) {
|
||||
args << "--enable-drag-drop";
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
if (m_appConfig.tlsEnabled()) {
|
||||
args << "--enable-crypto";
|
||||
args << "--tls-cert" << m_appConfig.tlsCertPath();
|
||||
}
|
||||
|
||||
#if defined(Q_OS_WIN)
|
||||
// on windows, the profile directory changes depending on the user that
|
||||
// launched the process (e.g. when launched with elevation). setting the
|
||||
// profile dir on launch ensures it uses the same profile dir is used
|
||||
// no matter how its relaunched.
|
||||
args << "--profile-dir" << m_pDeps->getProfileRoot();
|
||||
#endif
|
||||
|
||||
if (m_appConfig.preventSleep()) {
|
||||
args << "--prevent-sleep";
|
||||
}
|
||||
|
||||
if ((mode() == Mode::Client && !clientArgs(args, app)) ||
|
||||
(mode() == Mode::Server && !serverArgs(args, app))) {
|
||||
qDebug("failed to get args for core process, aborting start");
|
||||
return;
|
||||
}
|
||||
|
||||
qDebug("log level: %s", qPrintable(m_appConfig.logLevelText()));
|
||||
|
||||
if (m_appConfig.logToFile())
|
||||
qInfo("log file: %s", qPrintable(m_appConfig.logFilename()));
|
||||
|
||||
if (processMode == ProcessMode::kDesktop) {
|
||||
startDesktop(app, args);
|
||||
} else if (processMode == ProcessMode::kService) {
|
||||
startService(app, args);
|
||||
}
|
||||
|
||||
m_lastProcessMode = processMode;
|
||||
}
|
||||
|
||||
void CoreProcess::stop(std::optional<ProcessMode> processModeOption) {
|
||||
QMutexLocker locker(&m_processMutex);
|
||||
|
||||
const auto processMode =
|
||||
processModeOption.value_or(m_appConfig.processMode());
|
||||
|
||||
qInfo(
|
||||
"stopping core process (%s mode)",
|
||||
qPrintable(processModeToString(processMode)));
|
||||
|
||||
if (m_processState == ProcessState::Starting) {
|
||||
qDebug("core process is starting, cancelling");
|
||||
setProcessState(ProcessState::Stopped);
|
||||
} else if (m_processState != ProcessState::Stopped) {
|
||||
setProcessState(ProcessState::Stopping);
|
||||
|
||||
if (processMode == ProcessMode::kService) {
|
||||
stopService();
|
||||
} else if (processMode == ProcessMode::kDesktop) {
|
||||
stopDesktop();
|
||||
}
|
||||
|
||||
} else {
|
||||
qWarning("core process already stopped");
|
||||
}
|
||||
|
||||
setConnectionState(ConnectionState::Disconnected);
|
||||
}
|
||||
|
||||
void CoreProcess::restart() {
|
||||
using enum ProcessMode;
|
||||
|
||||
qDebug("restarting core process");
|
||||
|
||||
const auto processMode = m_appConfig.processMode();
|
||||
|
||||
if (m_lastProcessMode != processMode) {
|
||||
if (processMode == kDesktop) {
|
||||
qDebug("process mode changed to desktop, stopping service process");
|
||||
stop(kService);
|
||||
} else if (processMode == kService) {
|
||||
qDebug("process mode changed to service, stopping desktop process");
|
||||
stop(kDesktop);
|
||||
} else {
|
||||
qFatal("invalid process mode");
|
||||
}
|
||||
} else {
|
||||
// in service mode: though there is technically no need to stop the service
|
||||
// before restarting it, it does make for cleaner process state tracking,
|
||||
// especially if something goes wrong with starting the service.
|
||||
stop();
|
||||
}
|
||||
|
||||
start();
|
||||
}
|
||||
|
||||
void CoreProcess::cleanup() {
|
||||
qInfo("cleaning up core process");
|
||||
|
||||
const auto isDesktop = m_appConfig.processMode() == ProcessMode::kDesktop;
|
||||
const auto isRunning = m_processState == ProcessState::Started;
|
||||
if (isDesktop && isRunning) {
|
||||
stop();
|
||||
}
|
||||
|
||||
m_pDeps->ipcClient().disconnectFromHost();
|
||||
}
|
||||
|
||||
bool CoreProcess::serverArgs(QStringList &args, QString &app) {
|
||||
app = m_pDeps->appPath(m_appConfig.coreServerName());
|
||||
|
||||
if (!m_pDeps->fileExists(app)) {
|
||||
qFatal("core server binary does not exist");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (m_appConfig.logToFile()) {
|
||||
m_appConfig.persistLogDir();
|
||||
|
||||
args << "--log" << m_appConfig.logFilename();
|
||||
}
|
||||
|
||||
QString configFilename = persistConfig();
|
||||
if (configFilename.isEmpty()) {
|
||||
qFatal("config file name empty for server args");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (m_appConfig.invertConnection()) {
|
||||
qDebug("inverting server connection");
|
||||
|
||||
if (correctedAddress().isEmpty()) {
|
||||
emit error(Error::AddressMissing);
|
||||
qDebug("address is missing for server args");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// the address arg is dual purpose; when in listening mode, it's the address
|
||||
// that the server listens on. when tcp sockets are inverted, it connects to
|
||||
// that address. this is a bit confusing, and there should be probably be
|
||||
// different args for different purposes.
|
||||
args << "--address" << correctedInterface();
|
||||
|
||||
args << "-c" << configFilename;
|
||||
qInfo("config file: %s", qPrintable(configFilename));
|
||||
|
||||
if (kEnableActivation && !m_appConfig.serialKey().isEmpty()) {
|
||||
args << "--serial-key" << m_appConfig.serialKey();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CoreProcess::clientArgs(QStringList &args, QString &app) {
|
||||
app = m_pDeps->appPath(m_appConfig.coreClientName());
|
||||
|
||||
if (!m_pDeps->fileExists(app)) {
|
||||
qFatal("core client binary does not exist");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (m_appConfig.logToFile()) {
|
||||
m_appConfig.persistLogDir();
|
||||
args << "--log" << m_appConfig.logFilename();
|
||||
}
|
||||
|
||||
if (m_appConfig.languageSync()) {
|
||||
args << "--sync-language";
|
||||
}
|
||||
|
||||
if (m_appConfig.invertScrollDirection()) {
|
||||
args << "--invert-scroll";
|
||||
}
|
||||
|
||||
if (m_appConfig.invertConnection()) {
|
||||
qDebug("inverting client connection");
|
||||
args << "--host";
|
||||
args << ":" + QString::number(m_appConfig.port());
|
||||
} else {
|
||||
|
||||
if (correctedAddress().isEmpty()) {
|
||||
emit error(Error::AddressMissing);
|
||||
qDebug("address is missing for client args");
|
||||
return false;
|
||||
}
|
||||
|
||||
args << correctedAddress() + ":" + QString::number(m_appConfig.port());
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
QString CoreProcess::persistConfig() const {
|
||||
QString configFullPath;
|
||||
if (m_appConfig.useExternalConfig()) {
|
||||
return m_appConfig.configFile();
|
||||
}
|
||||
|
||||
for (auto path :
|
||||
{QStandardPaths::AppDataLocation, QStandardPaths::AppConfigLocation}) {
|
||||
auto configDirPath = QStandardPaths::writableLocation(path);
|
||||
if (!QDir().mkpath(configDirPath)) {
|
||||
qWarning("failed to create config folder: %s", qPrintable(configDirPath));
|
||||
continue;
|
||||
}
|
||||
|
||||
QFile configFile(configDirPath + "/" + kLastConfigFilename);
|
||||
if (!configFile.open(QIODevice::WriteOnly | QIODevice::Truncate)) {
|
||||
qWarning(
|
||||
"failed to open config file: %s", qPrintable(configFile.fileName()));
|
||||
continue;
|
||||
}
|
||||
|
||||
m_serverConfig.save(configFile);
|
||||
configFile.close();
|
||||
return configFile.fileName();
|
||||
}
|
||||
|
||||
qFatal("failed to persist config file");
|
||||
return "";
|
||||
}
|
||||
|
||||
QString CoreProcess::modeString() const {
|
||||
using enum Mode;
|
||||
|
||||
switch (m_mode) {
|
||||
case Server:
|
||||
return "server";
|
||||
case Client:
|
||||
return "client";
|
||||
default:
|
||||
qFatal("invalid core mode");
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
void CoreProcess::setConnectionState(ConnectionState state) {
|
||||
if (m_connectionState == state) {
|
||||
return;
|
||||
}
|
||||
|
||||
m_connectionState = state;
|
||||
emit connectionStateChanged(state);
|
||||
}
|
||||
|
||||
void CoreProcess::setProcessState(ProcessState state) {
|
||||
if (m_processState == state) {
|
||||
return;
|
||||
}
|
||||
|
||||
m_processState = state;
|
||||
emit processStateChanged(state);
|
||||
}
|
||||
|
||||
void CoreProcess::checkLogLine(const QString &line) {
|
||||
using enum ConnectionState;
|
||||
|
||||
if (line.contains("connected to server") || line.contains("has connected")) {
|
||||
setConnectionState(Connected);
|
||||
|
||||
} else if (line.contains("started server")) {
|
||||
setConnectionState(Listening);
|
||||
} else if (
|
||||
line.contains("disconnected from server") ||
|
||||
line.contains("process exited")) {
|
||||
setConnectionState(Disconnected);
|
||||
} else if (line.contains("connecting to")) {
|
||||
setConnectionState(Connecting);
|
||||
}
|
||||
|
||||
checkSecureSocket(line);
|
||||
|
||||
// subprocess (synergys, synergyc) is not allowed to show notifications
|
||||
// process the log from it and show notification from synergy instead
|
||||
#ifdef Q_OS_MAC
|
||||
checkOSXNotification(line);
|
||||
#endif
|
||||
}
|
||||
|
||||
bool CoreProcess::checkSecureSocket(const QString &line) {
|
||||
static const QString tlsCheckString = "network encryption protocol: ";
|
||||
const auto index = line.indexOf(tlsCheckString, 0, Qt::CaseInsensitive);
|
||||
if (index == -1) {
|
||||
return false;
|
||||
}
|
||||
|
||||
emit secureSocket(true);
|
||||
m_secureSocketVersion = line.mid(index + tlsCheckString.size());
|
||||
return true;
|
||||
}
|
||||
|
||||
#ifdef Q_OS_MAC
|
||||
void CoreProcess::checkOSXNotification(const QString &line) {
|
||||
static const QString needle = "OSX Notification: ";
|
||||
if (line.contains(needle) && line.contains('|')) {
|
||||
int delimiterPosition = line.indexOf('|');
|
||||
int start = line.indexOf(needle);
|
||||
QString title = line.mid(
|
||||
start + needle.length(), delimiterPosition - start - needle.length());
|
||||
QString body =
|
||||
line.mid(delimiterPosition + 1, line.length() - delimiterPosition);
|
||||
if (!showOSXNotification(title, body)) {
|
||||
qDebug("osx notification was not shown");
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
QString CoreProcess::correctedInterface() const {
|
||||
QString interface = wrapIpv6(m_appConfig.networkInterface());
|
||||
return interface + ":" + QString::number(m_appConfig.port());
|
||||
}
|
||||
|
||||
QString CoreProcess::correctedAddress() const { return wrapIpv6(m_address); }
|
||||
|
||||
} // namespace synergy::gui
|
||||
130
src/lib/gui/core/CoreProcess.h
Normal file
130
src/lib/gui/core/CoreProcess.h
Normal file
|
|
@ -0,0 +1,130 @@
|
|||
/*
|
||||
* synergy -- mouse and keyboard sharing utility
|
||||
* Copyright (C) 2024 Symless Ltd.
|
||||
*
|
||||
* This package is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* found in the file LICENSE that should have accompanied this file.
|
||||
*
|
||||
* This package is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "gui/config/IAppConfig.h"
|
||||
#include "gui/config/IServerConfig.h"
|
||||
#include "gui/ipc/QIpcClient.h"
|
||||
#include "gui/proxy/QProcessProxy.h"
|
||||
|
||||
#include <QMutex>
|
||||
#include <QObject>
|
||||
#include <QString>
|
||||
#include <QStringList>
|
||||
#include <memory>
|
||||
|
||||
namespace synergy::gui {
|
||||
|
||||
class CoreProcess : public QObject {
|
||||
using IServerConfig = synergy::gui::IServerConfig;
|
||||
using QProcessProxy = synergy::gui::proxy::QProcessProxy;
|
||||
using IQIpcClient = synergy::gui::ipc::IQIpcClient;
|
||||
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
struct Deps {
|
||||
virtual ~Deps() = default;
|
||||
virtual QProcessProxy &process() { return m_process; }
|
||||
virtual IQIpcClient &ipcClient() { return m_ipcClient; }
|
||||
virtual QString appPath(const QString &name) const;
|
||||
virtual bool fileExists(const QString &path) const;
|
||||
virtual QString getProfileRoot() const;
|
||||
|
||||
private:
|
||||
QProcessProxy m_process;
|
||||
QIpcClient m_ipcClient;
|
||||
};
|
||||
|
||||
enum class Mode { None, Client, Server };
|
||||
enum class Error { AddressMissing, StartFailed };
|
||||
enum class ProcessState { Starting, Started, Stopping, Stopped };
|
||||
enum class ConnectionState { Disconnected, Connecting, Connected, Listening };
|
||||
|
||||
explicit CoreProcess(
|
||||
IAppConfig &appConfig, IServerConfig &serverConfig,
|
||||
std::shared_ptr<Deps> deps = std::make_shared<Deps>());
|
||||
|
||||
void extracted(QString &app, QStringList &args);
|
||||
void start(std::optional<ProcessMode> processMode = std::nullopt);
|
||||
void stop(std::optional<ProcessMode> processMode = std::nullopt);
|
||||
void restart();
|
||||
void cleanup();
|
||||
|
||||
// getters
|
||||
Mode mode() const { return m_mode; }
|
||||
QString secureSocketVersion() const { return m_secureSocketVersion; }
|
||||
bool isStarted() const { return m_processState == ProcessState::Started; }
|
||||
ProcessState processState() const { return m_processState; }
|
||||
ConnectionState connectionState() const { return m_connectionState; }
|
||||
|
||||
// setters
|
||||
void setAddress(const QString &address) { m_address = address.trimmed(); }
|
||||
void setMode(Mode mode) { m_mode = mode; }
|
||||
|
||||
signals:
|
||||
void starting();
|
||||
void error(Error error);
|
||||
void logLine(const QString &line);
|
||||
void connectionStateChanged(ConnectionState state);
|
||||
void processStateChanged(ProcessState state);
|
||||
void secureSocket(bool enabled);
|
||||
|
||||
private slots:
|
||||
void onIpcClientServiceReady();
|
||||
void onIpcClientRead(const QString &text);
|
||||
void onIpcClientError(const QString &text) const;
|
||||
void onProcessFinished(int exitCode, QProcess::ExitStatus);
|
||||
void onProcessReadyReadStandardOutput();
|
||||
void onProcessReadyReadStandardError();
|
||||
|
||||
private:
|
||||
void startDesktop(const QString &app, const QStringList &args);
|
||||
void startService(const QString &app, const QStringList &args);
|
||||
void stopDesktop() const;
|
||||
void stopService();
|
||||
bool serverArgs(QStringList &args, QString &app);
|
||||
bool clientArgs(QStringList &args, QString &app);
|
||||
QString persistConfig() const;
|
||||
QString modeString() const;
|
||||
QString processModeString() const;
|
||||
void setConnectionState(ConnectionState state);
|
||||
void setProcessState(ProcessState state);
|
||||
void checkLogLine(const QString &line);
|
||||
bool checkSecureSocket(const QString &line);
|
||||
void handleLogLines(const QString &text);
|
||||
QString correctedInterface() const;
|
||||
QString correctedAddress() const;
|
||||
|
||||
#ifdef Q_OS_MAC
|
||||
void checkOSXNotification(const QString &line);
|
||||
#endif
|
||||
|
||||
IAppConfig &m_appConfig;
|
||||
IServerConfig &m_serverConfig;
|
||||
std::shared_ptr<Deps> m_pDeps;
|
||||
QString m_address;
|
||||
ProcessState m_processState = ProcessState::Stopped;
|
||||
ConnectionState m_connectionState = ConnectionState::Disconnected;
|
||||
Mode m_mode = Mode::None;
|
||||
QMutex m_processMutex;
|
||||
QString m_secureSocketVersion = "";
|
||||
std::optional<ProcessMode> m_lastProcessMode = std::nullopt;
|
||||
};
|
||||
|
||||
} // namespace synergy::gui
|
||||
|
|
@ -15,7 +15,7 @@
|
|||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "gui/CoreInterface.h"
|
||||
#include "gui/core/CoreTool.h"
|
||||
|
||||
#include "CommandProcess.h"
|
||||
|
||||
|
|
@ -32,28 +32,27 @@ static const char kSerialKeyFilename[] = "Synergy.subkey";
|
|||
static const char kSerialKeyFilename[] = ".synergy.subkey";
|
||||
#endif
|
||||
|
||||
QString CoreInterface::getProfileDir() const {
|
||||
QString CoreTool::getProfileDir() const {
|
||||
QStringList args("--get-profile-dir");
|
||||
return QDir::cleanPath(run(args));
|
||||
}
|
||||
|
||||
QString CoreInterface::getInstalledDir() const {
|
||||
QString CoreTool::getInstalledDir() const {
|
||||
QStringList args("--get-installed-dir");
|
||||
return QDir::cleanPath(run(args));
|
||||
}
|
||||
|
||||
QString CoreInterface::getArch() const {
|
||||
QString CoreTool::getArch() const {
|
||||
QStringList args("--get-arch");
|
||||
return run(args);
|
||||
}
|
||||
|
||||
QString CoreInterface::getSerialKeyFilePath() const {
|
||||
QString CoreTool::getSerialKeyFilePath() const {
|
||||
auto filename = getProfileDir() + QDir::separator() + kSerialKeyFilename;
|
||||
return QDir::cleanPath(filename);
|
||||
}
|
||||
|
||||
QString
|
||||
CoreInterface::run(const QStringList &args, const QString &input) const {
|
||||
QString CoreTool::run(const QStringList &args, const QString &input) const {
|
||||
QString program(QCoreApplication::applicationDirPath() + "/" + kCoreBinary);
|
||||
|
||||
CommandProcess commandProcess(program, args, input);
|
||||
|
|
@ -19,7 +19,7 @@
|
|||
|
||||
#include <QString>
|
||||
|
||||
class CoreInterface {
|
||||
class CoreTool {
|
||||
public:
|
||||
QString getProfileDir() const;
|
||||
QString getInstalledDir() const;
|
||||
90
src/lib/gui/core/ServerConnection.cpp
Normal file
90
src/lib/gui/core/ServerConnection.cpp
Normal file
|
|
@ -0,0 +1,90 @@
|
|||
/*
|
||||
* synergy -- mouse and keyboard sharing utility
|
||||
* Copyright (C) 2021 Symless Ltd.
|
||||
*
|
||||
* This package is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* 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 "ServerConnection.h"
|
||||
|
||||
#include "ServerMessage.h"
|
||||
#include "gui/config/AppConfig.h"
|
||||
|
||||
#include <QMessageBox>
|
||||
|
||||
namespace synergy::gui {
|
||||
|
||||
ServerConnection::ServerConnection(
|
||||
QWidget &parent, AppConfig &appConfig, IServerConfig &serverConfig)
|
||||
: m_parent(parent),
|
||||
m_appConfig(appConfig),
|
||||
m_serverConfig(serverConfig) {}
|
||||
|
||||
void ServerConnection::update(const QString &line) {
|
||||
ServerMessage message(line);
|
||||
|
||||
if (!m_appConfig.useExternalConfig() && message.isNewClientMessage() &&
|
||||
!m_ignoredClients.contains(message.getClientName())) {
|
||||
addClient(message.getClientName());
|
||||
}
|
||||
}
|
||||
|
||||
// TOOD: merge duplicated code between client and server connection
|
||||
bool ServerConnection::checkMainWindow() {
|
||||
bool result = m_parent.isActiveWindow();
|
||||
|
||||
if (m_parent.isMinimized() || m_parent.isHidden()) {
|
||||
m_parent.showNormal();
|
||||
m_parent.activateWindow();
|
||||
result = true;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
void ServerConnection::addClient(const QString &clientName) {
|
||||
if (m_serverConfig.isFull()) {
|
||||
qDebug(
|
||||
"server config full, skipping add client prompt for: %s",
|
||||
qPrintable(clientName));
|
||||
return;
|
||||
}
|
||||
|
||||
if (m_serverConfig.screenExists(clientName)) {
|
||||
qDebug(
|
||||
"client already added, skipping add client prompt for: %s",
|
||||
qPrintable(clientName));
|
||||
return;
|
||||
}
|
||||
|
||||
if (!checkMainWindow()) {
|
||||
qDebug(
|
||||
"main window not active, skipping add client prompt for: %s",
|
||||
qPrintable(clientName));
|
||||
return;
|
||||
}
|
||||
|
||||
QMessageBox message(&m_parent);
|
||||
message.addButton(QObject::tr("Ignore"), QMessageBox::RejectRole);
|
||||
message.addButton(QObject::tr("Add client"), QMessageBox::AcceptRole);
|
||||
message.setText(
|
||||
QObject::tr("Client with name '%1' wants to connect").arg(clientName));
|
||||
|
||||
if (message.exec() == QMessageBox::Accepted) {
|
||||
emit configureClient(clientName);
|
||||
} else {
|
||||
m_ignoredClients.append(clientName);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace synergy::gui
|
||||
|
|
@ -1,7 +1,6 @@
|
|||
/*
|
||||
* synergy -- mouse and keyboard sharing utility
|
||||
* Copyright (C) 2012-2021 Symless Ltd.
|
||||
* Copyright (C) 2008 Volker Lanz (vl@fidra.de)
|
||||
* Copyright (C) 2021 Symless Ltd.
|
||||
*
|
||||
* This package is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
|
|
@ -16,26 +15,36 @@
|
|||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef SERVERCONNECTION_H
|
||||
#define SERVERCONNECTION_H
|
||||
#pragma once
|
||||
|
||||
#include <QString>
|
||||
#include <QStringList>
|
||||
|
||||
class MainWindow;
|
||||
#include "gui/config/AppConfig.h"
|
||||
#include "gui/config/IServerConfig.h"
|
||||
|
||||
class ServerConnection {
|
||||
MainWindow &m_parent;
|
||||
QStringList m_ignoredClients;
|
||||
namespace synergy::gui {
|
||||
|
||||
class ServerConnection : public QObject {
|
||||
Q_OBJECT
|
||||
using IServerConfig = synergy::gui::IServerConfig;
|
||||
|
||||
public:
|
||||
explicit ServerConnection(MainWindow &parent);
|
||||
explicit ServerConnection(
|
||||
QWidget &parent, AppConfig &appConfig, IServerConfig &serverConfig);
|
||||
void update(const QString &line);
|
||||
|
||||
signals:
|
||||
void configureClient(const QString &clientName);
|
||||
|
||||
private:
|
||||
void addClient(const QString &clientName);
|
||||
void configureClient(const QString &clientName);
|
||||
bool checkMainWindow();
|
||||
|
||||
QWidget &m_parent;
|
||||
AppConfig &m_appConfig;
|
||||
IServerConfig &m_serverConfig;
|
||||
QStringList m_ignoredClients;
|
||||
};
|
||||
|
||||
#endif // SERVERCONNECTION_H
|
||||
} // namespace synergy::gui
|
||||
|
|
@ -1,7 +1,6 @@
|
|||
/*
|
||||
* synergy -- mouse and keyboard sharing utility
|
||||
* Copyright (C) 2012-2021 Symless Ltd.
|
||||
* Copyright (C) 2008 Volker Lanz (vl@fidra.de)
|
||||
* Copyright (C) 2021 Symless Ltd.
|
||||
*
|
||||
* This package is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
|
|
@ -15,8 +14,11 @@
|
|||
* 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 "ServerMessage.h"
|
||||
|
||||
namespace synergy::gui {
|
||||
|
||||
ServerMessage::ServerMessage(const QString &message)
|
||||
: m_message(message),
|
||||
m_clienName(parseClientName(message)) {}
|
||||
|
|
@ -50,3 +52,5 @@ QString ServerMessage::parseClientName(const QString &line) const {
|
|||
|
||||
return clientName;
|
||||
}
|
||||
|
||||
} // namespace synergy::gui
|
||||
|
|
@ -1,7 +1,6 @@
|
|||
/*
|
||||
* synergy -- mouse and keyboard sharing utility
|
||||
* Copyright (C) 2012-2021 Symless Ltd.
|
||||
* Copyright (C) 2008 Volker Lanz (vl@fidra.de)
|
||||
* Copyright (C) 2021 Symless Ltd.
|
||||
*
|
||||
* This package is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
|
|
@ -16,11 +15,12 @@
|
|||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef SERVERMESSAGE_H
|
||||
#define SERVERMESSAGE_H
|
||||
#pragma once
|
||||
|
||||
#include <QString>
|
||||
|
||||
namespace synergy::gui {
|
||||
|
||||
class ServerMessage {
|
||||
QString m_message;
|
||||
QString m_clienName;
|
||||
|
|
@ -39,4 +39,4 @@ private:
|
|||
QString parseClientName(const QString &line) const;
|
||||
};
|
||||
|
||||
#endif // SERVERMESSAGE_H
|
||||
} // namespace synergy::gui
|
||||
|
|
@ -15,18 +15,4 @@
|
|||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <QString>
|
||||
|
||||
namespace synergy::gui {
|
||||
|
||||
class IAppConfig {
|
||||
public:
|
||||
virtual ~IAppConfig() = default;
|
||||
virtual QString tlsCertPath() const = 0;
|
||||
virtual QString tlsKeyLength() const = 0;
|
||||
virtual bool tlsEnabled() const = 0;
|
||||
};
|
||||
|
||||
} // namespace synergy::gui
|
||||
// empty .cpp for qt moc
|
||||
43
src/lib/gui/ipc/IQIpcClient.h
Normal file
43
src/lib/gui/ipc/IQIpcClient.h
Normal file
|
|
@ -0,0 +1,43 @@
|
|||
/*
|
||||
* 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 <QObject>
|
||||
#include <QString>
|
||||
|
||||
#include "gui/config/ElevateMode.h"
|
||||
|
||||
namespace synergy::gui::ipc {
|
||||
|
||||
class IQIpcClient : public QObject {
|
||||
Q_OBJECT
|
||||
public:
|
||||
~IQIpcClient() override = default;
|
||||
virtual void sendHello() const = 0;
|
||||
virtual void
|
||||
sendCommand(const QString &command, ElevateMode elevate) const = 0;
|
||||
virtual void connectToHost() = 0;
|
||||
virtual void disconnectFromHost() = 0;
|
||||
virtual bool isConnected() const = 0;
|
||||
|
||||
signals:
|
||||
void read(const QString &text);
|
||||
void serviceReady();
|
||||
};
|
||||
|
||||
} // namespace synergy::gui::ipc
|
||||
|
|
@ -16,97 +16,88 @@
|
|||
*/
|
||||
|
||||
#include "IpcReader.h"
|
||||
|
||||
#include "Logger.h"
|
||||
#include "byte_utils.h"
|
||||
#include "global/Ipc.h"
|
||||
|
||||
#include <QByteArray>
|
||||
#include <QMutex>
|
||||
#include <QTcpSocket>
|
||||
#include <iostream>
|
||||
|
||||
using namespace synergy::gui;
|
||||
|
||||
IpcReader::IpcReader(QTcpSocket *socket) : m_Socket(socket) {}
|
||||
|
||||
IpcReader::~IpcReader() {}
|
||||
|
||||
void IpcReader::start() {
|
||||
connect(m_Socket, SIGNAL(readyRead()), this, SLOT(read()));
|
||||
void IpcReader::start() const {
|
||||
connect(
|
||||
m_Socket, &QTcpSocket::readyRead, this, &IpcReader::onSocketReadyRead);
|
||||
}
|
||||
|
||||
void IpcReader::stop() {
|
||||
disconnect(m_Socket, SIGNAL(readyRead()), this, SLOT(read()));
|
||||
void IpcReader::stop() const {
|
||||
disconnect(
|
||||
m_Socket, &QTcpSocket::readyRead, this, &IpcReader::onSocketReadyRead);
|
||||
}
|
||||
|
||||
void IpcReader::read() {
|
||||
void IpcReader::onSocketReadyRead() {
|
||||
QMutexLocker locker(&m_Mutex);
|
||||
std::cout << "ready read" << std::endl;
|
||||
logVerbose("ready read");
|
||||
|
||||
while (m_Socket->bytesAvailable()) {
|
||||
std::cout << "bytes available" << std::endl;
|
||||
logVerbose("bytes available");
|
||||
|
||||
char codeBuf[5];
|
||||
readStream(codeBuf, 4);
|
||||
codeBuf[4] = 0;
|
||||
std::cout << "ipc read: " << codeBuf << std::endl;
|
||||
logVerbose(QString("ipc read: %1").arg(codeBuf));
|
||||
|
||||
if (memcmp(codeBuf, kIpcMsgLogLine, 4) == 0) {
|
||||
std::cout << "reading log line" << std::endl;
|
||||
logVerbose("reading log line");
|
||||
|
||||
char lenBuf[4];
|
||||
readStream(lenBuf, 4);
|
||||
int len = bytesToInt(lenBuf, 4);
|
||||
|
||||
char *data = new char[len];
|
||||
readStream(data, len);
|
||||
QString line = QString::fromUtf8(data, len);
|
||||
delete[] data;
|
||||
std::vector<char> dataBuf(len);
|
||||
readStream(dataBuf.data(), len);
|
||||
QString text = QString::fromUtf8(dataBuf.data(), len);
|
||||
|
||||
readLogLine(line);
|
||||
emit read(text);
|
||||
} else if (memcmp(codeBuf, kIpcMsgHelloBack, 4) == 0) {
|
||||
logVerbose("reading hello back");
|
||||
emit helloBack();
|
||||
} else {
|
||||
std::cerr << "aborting, message invalid" << std::endl;
|
||||
qCritical("aborting ipc read, message invalid");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
std::cout << "read done" << std::endl;
|
||||
logVerbose("read done");
|
||||
}
|
||||
|
||||
bool IpcReader::readStream(char *buffer, int length) {
|
||||
std::cout << "reading stream" << std::endl;
|
||||
logVerbose("reading stream");
|
||||
|
||||
int read = 0;
|
||||
while (read < length) {
|
||||
int ask = length - read;
|
||||
if (m_Socket->bytesAvailable() < ask) {
|
||||
std::cout << "buffer too short, waiting" << std::endl;
|
||||
logVerbose("buffer too short, waiting");
|
||||
m_Socket->waitForReadyRead(-1);
|
||||
}
|
||||
|
||||
int got = m_Socket->read(buffer, ask);
|
||||
auto got = m_Socket->read(buffer, ask);
|
||||
read += got;
|
||||
|
||||
std::cout << "> ask=" << ask << " got=" << got << " read=" << read
|
||||
<< std::endl;
|
||||
logVerbose(QString("ask=%1 got=%2 read=%3").arg(ask).arg(got).arg(read));
|
||||
|
||||
if (got == -1) {
|
||||
std::cout << "socket ended, aborting" << std::endl;
|
||||
logVerbose("socket ended, aborting");
|
||||
return false;
|
||||
} else if (length - read > 0) {
|
||||
std::cout << "more remains, seek to " << got << std::endl;
|
||||
logVerbose(QString("more remains, seek to %1").arg(got));
|
||||
buffer += got;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
int IpcReader::bytesToInt(const char *buffer, int size) {
|
||||
if (size == 1) {
|
||||
return (unsigned char)buffer[0];
|
||||
} else if (size == 2) {
|
||||
return (((unsigned char)buffer[0]) << 8) + (unsigned char)buffer[1];
|
||||
} else if (size == 4) {
|
||||
return (((unsigned char)buffer[0]) << 24) +
|
||||
(((unsigned char)buffer[1]) << 16) +
|
||||
(((unsigned char)buffer[2]) << 8) + (unsigned char)buffer[3];
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
|
@ -26,20 +26,20 @@ class IpcReader : public QObject {
|
|||
Q_OBJECT;
|
||||
|
||||
public:
|
||||
IpcReader(QTcpSocket *socket);
|
||||
virtual ~IpcReader();
|
||||
void start();
|
||||
void stop();
|
||||
explicit IpcReader(QTcpSocket *socket);
|
||||
~IpcReader() override = default;
|
||||
void start() const;
|
||||
void stop() const;
|
||||
|
||||
signals:
|
||||
void readLogLine(const QString &text);
|
||||
void read(const QString &text);
|
||||
void helloBack();
|
||||
|
||||
private:
|
||||
bool readStream(char *buffer, int length);
|
||||
int bytesToInt(const char *buffer, int size);
|
||||
|
||||
private slots:
|
||||
void read();
|
||||
void onSocketReadyRead();
|
||||
|
||||
private:
|
||||
QTcpSocket *m_Socket;
|
||||
|
|
@ -28,7 +28,7 @@ public:
|
|||
}
|
||||
virtual ~QDataStreamProxy() = default;
|
||||
|
||||
virtual qint64 writeRawData(const char *data, qint64 len) {
|
||||
virtual qint64 writeRawData(const char *data, int len) {
|
||||
assert(m_Stream);
|
||||
return m_Stream->writeRawData(data, len);
|
||||
}
|
||||
161
src/lib/gui/ipc/QIpcClient.cpp
Normal file
161
src/lib/gui/ipc/QIpcClient.cpp
Normal file
|
|
@ -0,0 +1,161 @@
|
|||
/*
|
||||
* synergy -- mouse and keyboard sharing utility
|
||||
* Copyright (C) 2012 Symless Ltd.
|
||||
*
|
||||
* This package is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* 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 "QIpcClient.h"
|
||||
|
||||
#include "IpcReader.h"
|
||||
#include "byte_utils.h"
|
||||
#include "global/Ipc.h"
|
||||
|
||||
#include <QDataStream>
|
||||
#include <QHostAddress>
|
||||
#include <QTimer>
|
||||
#include <memory>
|
||||
|
||||
const auto kRetryInterval = 1000;
|
||||
const auto kConnectTimeout = 5000;
|
||||
|
||||
using namespace synergy::gui;
|
||||
|
||||
QIpcClient::QIpcClient(const StreamProvider &streamProvider)
|
||||
: m_streamProvider(streamProvider) {
|
||||
|
||||
m_pSocket = std::make_unique<QTcpSocket>();
|
||||
|
||||
if (!m_streamProvider) {
|
||||
m_streamProvider = [this]() {
|
||||
return std::make_shared<QDataStreamProxy>(m_pSocket.get());
|
||||
};
|
||||
}
|
||||
|
||||
connect(
|
||||
m_pSocket.get(), &QTcpSocket::connected, this,
|
||||
&QIpcClient::onSocketConnected);
|
||||
connect(
|
||||
m_pSocket.get(), &QTcpSocket::errorOccurred, this,
|
||||
&QIpcClient::onSocketError);
|
||||
|
||||
m_pReader = std::make_unique<IpcReader>(m_pSocket.get());
|
||||
connect(
|
||||
m_pReader.get(), &IpcReader::read, this, //
|
||||
&QIpcClient::onIpcReaderRead);
|
||||
connect(
|
||||
m_pReader.get(), &IpcReader::helloBack, this,
|
||||
&QIpcClient::onIpcReaderHelloBack);
|
||||
}
|
||||
|
||||
void QIpcClient::onSocketConnected() const { sendHello(); }
|
||||
|
||||
void QIpcClient::connectToHost() {
|
||||
m_isConnecting = true;
|
||||
qInfo("connecting to background service...");
|
||||
const auto port = static_cast<quint16>(kIpcPort);
|
||||
m_pSocket->connectToHost(QHostAddress(QHostAddress::LocalHost), port);
|
||||
|
||||
if (!m_readerStarted) {
|
||||
m_pReader->start();
|
||||
m_readerStarted = true;
|
||||
}
|
||||
|
||||
QTimer::singleShot(kConnectTimeout, this, [this]() {
|
||||
if (!m_isConnected) {
|
||||
qCritical("ipc connection timeout");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void QIpcClient::disconnectFromHost() {
|
||||
m_isConnecting = false;
|
||||
qInfo("disconnected from background service");
|
||||
m_pReader->stop();
|
||||
m_pSocket->close();
|
||||
m_isConnected = false;
|
||||
}
|
||||
|
||||
void QIpcClient::onSocketError(QAbstractSocket::SocketError socketError) const {
|
||||
QString text;
|
||||
switch (socketError) {
|
||||
case 0:
|
||||
text = "connection refused";
|
||||
break;
|
||||
case 1:
|
||||
text = "remote host closed";
|
||||
break;
|
||||
default:
|
||||
text = QString("code=%1").arg(socketError);
|
||||
break;
|
||||
}
|
||||
|
||||
qWarning("ipc connection error, %s", qUtf8Printable(text));
|
||||
|
||||
QTimer::singleShot(kRetryInterval, this, &QIpcClient::onRetryConnect);
|
||||
}
|
||||
|
||||
void QIpcClient::onRetryConnect() {
|
||||
if (m_isConnected) {
|
||||
qDebug("ipc already connected, skipping retry");
|
||||
return;
|
||||
} else if (!m_isConnecting) {
|
||||
qDebug("ipc not connecting, skipping retry");
|
||||
return;
|
||||
}
|
||||
|
||||
qInfo("retrying connection to background service...");
|
||||
connectToHost();
|
||||
}
|
||||
|
||||
void QIpcClient::sendHello() const {
|
||||
qDebug("sending ipc hello message");
|
||||
auto stream = m_streamProvider();
|
||||
stream->writeRawData(kIpcMsgHello, 4);
|
||||
|
||||
char typeBuf[1];
|
||||
typeBuf[0] = static_cast<char>(IpcClientType::GUI);
|
||||
stream->writeRawData(typeBuf, 1);
|
||||
}
|
||||
|
||||
void QIpcClient::sendCommand(
|
||||
const QString &command, ElevateMode const elevate) const {
|
||||
qDebug("sending ipc command: %s", qUtf8Printable(command));
|
||||
|
||||
auto stream = m_streamProvider();
|
||||
stream->writeRawData(kIpcMsgCommand, 4);
|
||||
|
||||
std::string stdStringCommand = command.toStdString();
|
||||
const char *charCommand = stdStringCommand.c_str();
|
||||
auto length = static_cast<int>(stdStringCommand.length());
|
||||
|
||||
QByteArray lenBuf = intToBytes(length);
|
||||
if (lenBuf.size() != 4) {
|
||||
qFatal("unexpected int buffer size: %lld", lenBuf.size());
|
||||
}
|
||||
stream->writeRawData(lenBuf, 4);
|
||||
stream->writeRawData(charCommand, length);
|
||||
|
||||
char elevateBuf[1];
|
||||
// see enum ElevateMode documentation for why this flag is mapped this way
|
||||
elevateBuf[0] = (elevate == ElevateMode::kAlways) ? 1 : 0;
|
||||
stream->writeRawData(elevateBuf, 1);
|
||||
}
|
||||
|
||||
void QIpcClient::onIpcReaderHelloBack() {
|
||||
qDebug("ipc hello back received");
|
||||
m_isConnected = true;
|
||||
serviceReady();
|
||||
}
|
||||
|
||||
void QIpcClient::onIpcReaderRead(const QString &text) { emit read(text); }
|
||||
|
|
@ -21,46 +21,41 @@
|
|||
#include <QDataStream>
|
||||
#include <QObject>
|
||||
#include <QTcpSocket>
|
||||
#include <memory>
|
||||
|
||||
#include "ElevateMode.h"
|
||||
#include "IpcReader.h"
|
||||
#include "QDataStreamProxy.h"
|
||||
#include "gui/config/ElevateMode.h"
|
||||
#include "gui/ipc/IQIpcClient.h"
|
||||
|
||||
class IpcReader;
|
||||
|
||||
class QIpcClient : public QObject {
|
||||
class QIpcClient : public synergy::gui::ipc::IQIpcClient {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
using StreamProvider = std::function<std::shared_ptr<QDataStreamProxy>()>;
|
||||
|
||||
explicit QIpcClient(const StreamProvider &streamProvider = nullptr);
|
||||
~QIpcClient() override;
|
||||
|
||||
void sendHello();
|
||||
void sendCommand(const QString &command, ElevateMode elevate);
|
||||
void connectToHost();
|
||||
void disconnectFromHost();
|
||||
|
||||
public slots:
|
||||
void retryConnect();
|
||||
|
||||
private:
|
||||
void intToBytes(int value, char *buffer, int size);
|
||||
void sendHello() const override;
|
||||
void sendCommand(const QString &command, ElevateMode elevate) const override;
|
||||
void connectToHost() override;
|
||||
void disconnectFromHost() override;
|
||||
bool isConnected() const override { return m_isConnected; }
|
||||
|
||||
private slots:
|
||||
void connected();
|
||||
void error(QAbstractSocket::SocketError error);
|
||||
void handleReadLogLine(const QString &text);
|
||||
|
||||
signals:
|
||||
void readLogLine(const QString &text);
|
||||
void infoMessage(const QString &text);
|
||||
void errorMessage(const QString &text);
|
||||
void onRetryConnect();
|
||||
void onSocketConnected() const;
|
||||
void onIpcReaderHelloBack();
|
||||
void onSocketError(QAbstractSocket::SocketError error) const;
|
||||
void onIpcReaderRead(const QString &text);
|
||||
|
||||
private:
|
||||
QTcpSocket *m_Socket;
|
||||
IpcReader *m_Reader;
|
||||
bool m_ReaderStarted;
|
||||
bool m_Enabled;
|
||||
StreamProvider m_StreamProvider;
|
||||
std::unique_ptr<QTcpSocket> m_pSocket;
|
||||
std::unique_ptr<IpcReader> m_pReader;
|
||||
bool m_readerStarted = false;
|
||||
StreamProvider m_streamProvider;
|
||||
bool m_isConnected = false;
|
||||
bool m_isConnecting = false;
|
||||
};
|
||||
|
|
@ -52,6 +52,6 @@ signals:
|
|||
void invalidLicense() const;
|
||||
|
||||
private:
|
||||
bool m_enabled = kLicensingEnabled;
|
||||
bool m_enabled = synergy::gui::kEnableActivation;
|
||||
License m_license = License::invalid();
|
||||
};
|
||||
|
|
@ -35,6 +35,7 @@ QString licenseNotice(const License &license) {
|
|||
return subscriptionLicenseNotice(license);
|
||||
} else {
|
||||
qFatal("license notice only for time limited licenses");
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -17,75 +17,100 @@
|
|||
|
||||
#include "messages.h"
|
||||
|
||||
#include "Logger.h"
|
||||
#include "constants.h"
|
||||
#include "styles.h"
|
||||
|
||||
#include <QAction>
|
||||
#include <QDateTime>
|
||||
#include <QDir>
|
||||
#include <QMessageBox>
|
||||
#include <QTime>
|
||||
#include <memory>
|
||||
|
||||
namespace synergy::gui::messages {
|
||||
|
||||
struct Errors {
|
||||
static std::unique_ptr<QMessageBox> s_criticalMessage;
|
||||
static QStringList s_ignoredErrors;
|
||||
};
|
||||
|
||||
std::unique_ptr<QMessageBox> Errors::s_criticalMessage;
|
||||
QStringList Errors::s_ignoredErrors;
|
||||
|
||||
void showErrorDialog(
|
||||
const QString &message, const QMessageLogContext &context, QtMsgType type) {
|
||||
auto filename = QFileInfo(context.file).fileName();
|
||||
auto contextString = QString("%1:%2").arg(filename).arg(context.line);
|
||||
|
||||
auto title = type == QtFatalMsg ? "Fatal error" : "Critical error";
|
||||
QString text;
|
||||
if (type == QtFatalMsg) {
|
||||
text = "<p>Sorry, a fatal error has occurred and the application must "
|
||||
"now exit.</p>";
|
||||
} else {
|
||||
text = "<p>Sorry, a critical error has occurred.</p>";
|
||||
}
|
||||
|
||||
if (kLicensedProduct) {
|
||||
text += QString(R"(<p>Please <a href="%1" style="color: %2">contact us</a>)"
|
||||
" and copy/paste the following error:</p>")
|
||||
.arg(kUrlContact, kColorSecondary);
|
||||
} else {
|
||||
text +=
|
||||
QString(R"(<p>Please <a href="%1" style="color: %2">report a bug</a>)"
|
||||
" and copy/paste the following error:</p>")
|
||||
.arg(kUrlBugReport, kColorSecondary);
|
||||
}
|
||||
|
||||
text += QString("<pre>%3\n\n%4</pre>").arg(message, contextString);
|
||||
|
||||
if (type == QtFatalMsg) {
|
||||
// create a blocking message box for fatal errors, as we want to wait
|
||||
// until the dialog is dismissed before aborting the app.
|
||||
QMessageBox::critical(nullptr, title, text);
|
||||
} else if (!Errors::s_ignoredErrors.contains(message)) {
|
||||
// prevent message boxes piling up by deleting the last one if it exists.
|
||||
// if none exists yet, then nothing will happen.
|
||||
Errors::s_criticalMessage.reset();
|
||||
|
||||
// as we don't abort for critical messages, create a new non-blocking
|
||||
// message box. this is so that we don't block the message handler; if we
|
||||
// did, we would prevent new messages from being logged properly.
|
||||
// the memory will stay allocated until the app exits, which is acceptable.
|
||||
Errors::s_criticalMessage = std::make_unique<QMessageBox>(
|
||||
QMessageBox::Critical, title, text,
|
||||
QMessageBox::Ok | QMessageBox::Ignore);
|
||||
Errors::s_criticalMessage->open();
|
||||
|
||||
QAction::connect(
|
||||
Errors::s_criticalMessage.get(), &QMessageBox::finished, //
|
||||
[message](int result) {
|
||||
if (result == QMessageBox::Ignore) {
|
||||
Errors::s_ignoredErrors.append(message);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
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 : "";
|
||||
Logger::instance().handleMessage(type, context, message);
|
||||
|
||||
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;
|
||||
if (type == QtFatalMsg || type == QtCriticalMsg) {
|
||||
showErrorDialog(message, context, type);
|
||||
}
|
||||
|
||||
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.
|
||||
// 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.
|
||||
//
|
||||
// hint: if you don't want to crash, but still want to show an error
|
||||
// message, use `qCritical()` instead of `qFatal()`. you should use
|
||||
// fatal errors when the app is in an unrecoverable state; i.e. it cannot
|
||||
// function correctly in it's current state and must be restarted.
|
||||
abort();
|
||||
}
|
||||
}
|
||||
|
|
@ -143,16 +168,16 @@ void showDevThanks(QWidget *parent, const QString &productName) {
|
|||
|
||||
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>)"
|
||||
QString("<p>Thanks for using %1.</p>"
|
||||
"<p>If you enjoy using this app, you can support the <br />"
|
||||
"developers by "
|
||||
R"(<a href="%2" style="color: %3")>purchasing a license</a>)"
|
||||
" or "
|
||||
R"(<a href="%3" style="color: %4")>contributing code</a>.)"
|
||||
R"(<a href="%4" style="color: %5")>contributing code</a>.)"
|
||||
"</p>"
|
||||
"<p>This message will only appear once.</p>")
|
||||
.arg(
|
||||
productName, kUrlPurchase, kColorSecondary, kUrlContribute,
|
||||
productName, kUrlPurchase, kColorSecondary, kUrlGitHub,
|
||||
kColorSecondary));
|
||||
}
|
||||
|
||||
|
|
|
|||
63
src/lib/gui/proxy/QProcessProxy.cpp
Normal file
63
src/lib/gui/proxy/QProcessProxy.cpp
Normal file
|
|
@ -0,0 +1,63 @@
|
|||
/*
|
||||
* 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 "QProcessProxy.h"
|
||||
|
||||
namespace synergy::gui::proxy {
|
||||
|
||||
void QProcessProxy::create() {
|
||||
m_pProcess = std::make_unique<QProcess>();
|
||||
|
||||
connect(
|
||||
m_pProcess.get(), &QProcess::finished, this,
|
||||
[this](int exitCode, QProcess::ExitStatus exitStatus) {
|
||||
emit finished(exitCode, exitStatus);
|
||||
});
|
||||
|
||||
connect(
|
||||
m_pProcess.get(), &QProcess::readyReadStandardOutput, //
|
||||
this, [this]() { emit readyReadStandardOutput(); });
|
||||
|
||||
connect(
|
||||
m_pProcess.get(), &QProcess::readyReadStandardError, //
|
||||
this, [this]() { emit readyReadStandardError(); });
|
||||
}
|
||||
|
||||
QProcessProxy::operator bool() const { return m_pProcess.get(); }
|
||||
|
||||
void QProcessProxy::start(
|
||||
const QString &program, const QStringList &arguments) {
|
||||
m_pProcess->start(program, arguments);
|
||||
}
|
||||
|
||||
bool QProcessProxy::waitForStarted() { return m_pProcess->waitForStarted(); }
|
||||
|
||||
QProcess::ProcessState QProcessProxy::state() const {
|
||||
return m_pProcess->state();
|
||||
}
|
||||
|
||||
void QProcessProxy::close() { m_pProcess->close(); }
|
||||
|
||||
QString QProcessProxy::readAllStandardOutput() {
|
||||
return m_pProcess->readAllStandardOutput();
|
||||
}
|
||||
|
||||
QString QProcessProxy::readAllStandardError() {
|
||||
return m_pProcess->readAllStandardError();
|
||||
}
|
||||
|
||||
} // namespace synergy::gui::proxy
|
||||
48
src/lib/gui/proxy/QProcessProxy.h
Normal file
48
src/lib/gui/proxy/QProcessProxy.h
Normal file
|
|
@ -0,0 +1,48 @@
|
|||
/*
|
||||
* synergy -- mouse and keyboard sharing utility
|
||||
* Copyright (C) 2024 Symless Ltd.
|
||||
*
|
||||
* This package is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* found in the file LICENSE that should have accompanied this file.
|
||||
*
|
||||
* This package is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <QObject>
|
||||
#include <QProcess>
|
||||
|
||||
namespace synergy::gui::proxy {
|
||||
|
||||
class QProcessProxy : public QObject {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
~QProcessProxy() override = default;
|
||||
explicit virtual operator bool() const;
|
||||
virtual void create();
|
||||
virtual void start(const QString &program, const QStringList &arguments = {});
|
||||
virtual bool waitForStarted();
|
||||
virtual QProcess::ProcessState state() const;
|
||||
virtual void close();
|
||||
virtual QString readAllStandardOutput();
|
||||
virtual QString readAllStandardError();
|
||||
|
||||
signals:
|
||||
void finished(int exitCode, QProcess::ExitStatus exitStatus);
|
||||
void readyReadStandardOutput();
|
||||
void readyReadStandardError();
|
||||
|
||||
private:
|
||||
std::unique_ptr<QProcess> m_pProcess;
|
||||
};
|
||||
|
||||
} // namespace synergy::gui::proxy
|
||||
27
src/lib/gui/string_utils.h
Normal file
27
src/lib/gui/string_utils.h
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
/*
|
||||
* synergy -- mouse and keyboard sharing utility
|
||||
* Copyright (C) 2024 Symless
|
||||
*
|
||||
* 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>
|
||||
|
||||
/**
|
||||
* @brief Useful for environment variables that have string boolean values.
|
||||
*/
|
||||
inline bool strToTrue(const QString &str) {
|
||||
return str.toLower() == "true" || str == "1";
|
||||
}
|
||||
|
|
@ -19,6 +19,8 @@
|
|||
|
||||
#include <QString>
|
||||
|
||||
namespace synergy::gui {
|
||||
|
||||
const auto kColorWhite = "#ffffff";
|
||||
const auto kColorPrimary = "#ff7c00";
|
||||
const auto kColorSecondary = "#4285f4";
|
||||
|
|
@ -47,3 +49,5 @@ const auto kStyleNoticeLabel = //
|
|||
QString("padding: 3px 5px; border-radius: 3px;"
|
||||
"background-color: %1; color: %2")
|
||||
.arg(kColorNotice, kColorWhite);
|
||||
|
||||
} // namespace synergy::gui
|
||||
|
|
|
|||
|
|
@ -22,13 +22,10 @@
|
|||
#include <QCoreApplication>
|
||||
#include <QDir>
|
||||
#include <QProcess>
|
||||
#include <optional>
|
||||
|
||||
// RSA Bit length (e.g. 1024/2048/4096)
|
||||
static const char *const kCertificateKeyLength = "rsa:";
|
||||
|
||||
// fingerprint hashing algorithm
|
||||
static const char *const kCertificateHashAlgorithm = "-sha256";
|
||||
|
||||
static const char *const kCertificateLifetime = "365";
|
||||
static const char *const kCertificateSubjectInfo = "/CN=Synergy";
|
||||
static const char *const kCertificateFilename = "Synergy.pem";
|
||||
|
|
@ -42,9 +39,10 @@ static const char *const kConfigFile = "synergy.conf";
|
|||
static const char *const kUnixOpenSslCommand = "openssl";
|
||||
#endif
|
||||
|
||||
namespace synergy::gui {
|
||||
#if defined(Q_OS_WIN)
|
||||
|
||||
namespace synergy::gui {
|
||||
|
||||
QString openSslWindowsDir() {
|
||||
|
||||
auto appDir = QDir(QCoreApplication::applicationDirPath());
|
||||
|
|
@ -83,16 +81,16 @@ QString openSslWindowsBinary() {
|
|||
return path;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
} // namespace synergy::gui
|
||||
|
||||
using namespace synergy::gui;
|
||||
|
||||
#endif
|
||||
|
||||
TlsCertificate::TlsCertificate(QObject *parent) : QObject(parent) {
|
||||
m_ProfileDir = m_CoreInterface.getProfileDir();
|
||||
if (m_ProfileDir.isEmpty()) {
|
||||
emit error(tr("Failed to get profile directory."));
|
||||
m_profileDir = m_coreTool.getProfileDir();
|
||||
if (m_profileDir.isEmpty()) {
|
||||
qCritical("empty profile directory result");
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -108,47 +106,72 @@ bool TlsCertificate::runTool(const QStringList &args) {
|
|||
#if defined(Q_OS_WIN)
|
||||
auto openSslDir = QDir(openSslWindowsDir());
|
||||
auto config = QDir::cleanPath(openSslDir.filePath(kConfigFile));
|
||||
if (!QFile::exists(config)) {
|
||||
qDebug("openssl config file not found: %s", qUtf8Printable(config));
|
||||
|
||||
// if the expected production file location doesn't exist, try the dev path.
|
||||
config = QDir::cleanPath(QString("res/openssl/%1").arg(kConfigFile));
|
||||
|
||||
// if it still isn't there, then there's something seriously wrong.
|
||||
if (!QFile::exists(config)) {
|
||||
qFatal() << "openssl config file not found: " << config;
|
||||
}
|
||||
}
|
||||
|
||||
environment << QString("OPENSSL_CONF=%1").arg(config);
|
||||
#endif
|
||||
|
||||
qDebug(
|
||||
"running: %s %s", qUtf8Printable(program),
|
||||
qUtf8Printable(args.join(" ")));
|
||||
|
||||
QProcess process;
|
||||
|
||||
for (const auto &envVar : environment) {
|
||||
qDebug("setting env var %s", qUtf8Printable(envVar));
|
||||
}
|
||||
|
||||
process.setEnvironment(environment);
|
||||
|
||||
process.start(program, args);
|
||||
|
||||
bool success = process.waitForStarted();
|
||||
|
||||
QString standardError;
|
||||
QString stderrOutput;
|
||||
if (success && process.waitForFinished()) {
|
||||
m_ToolOutput = process.readAllStandardOutput().trimmed();
|
||||
standardError = process.readAllStandardError().trimmed();
|
||||
m_toolStdout = process.readAllStandardOutput().trimmed();
|
||||
stderrOutput = process.readAllStandardError().trimmed();
|
||||
}
|
||||
|
||||
if (int code = process.exitCode(); !success || code != 0) {
|
||||
emit error(QString("SSL tool failed: %1\n\nCode: %2\nError: %3")
|
||||
.arg(program)
|
||||
.arg(process.exitCode())
|
||||
.arg(standardError.isEmpty() ? "Unknown" : standardError));
|
||||
qDebug(
|
||||
"openssl failed with code %d: %s", code, qUtf8Printable(stderrOutput));
|
||||
|
||||
qCritical(
|
||||
"failed to generate TLS certificate:\n\n%s",
|
||||
qUtf8Printable(stderrOutput));
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void TlsCertificate::generateCertificate(
|
||||
const QString &path, const QString &keyLength, bool forceGen) {
|
||||
bool TlsCertificate::generateCertificate(const QString &path, int keyLength) {
|
||||
QString sslDirPath =
|
||||
QString("%1%2%3").arg(m_ProfileDir).arg(QDir::separator()).arg(kSslDir);
|
||||
QString("%1%2%3").arg(m_profileDir).arg(QDir::separator()).arg(kSslDir);
|
||||
|
||||
QString filename = QString("%1%2%3")
|
||||
QString defaultPath = QString("%1%2%3")
|
||||
.arg(sslDirPath)
|
||||
.arg(QDir::separator())
|
||||
.arg(kCertificateFilename);
|
||||
|
||||
QString keySize = kCertificateKeyLength + keyLength;
|
||||
QString keySize = kCertificateKeyLength + QString::number(keyLength);
|
||||
|
||||
const QString pathToUse = path.isEmpty() ? filename : path;
|
||||
const QString pathToUse =
|
||||
QDir::cleanPath(path.isEmpty() ? defaultPath : path);
|
||||
|
||||
qDebug("generating tls certificate: %s", qUtf8Printable(pathToUse));
|
||||
|
||||
if (QFile file(pathToUse); !file.exists() || forceGen) {
|
||||
QStringList arguments;
|
||||
|
||||
// self signed certificate
|
||||
|
|
@ -182,18 +205,19 @@ void TlsCertificate::generateCertificate(
|
|||
arguments.append("-out");
|
||||
arguments.append(pathToUse);
|
||||
|
||||
if (!runTool(arguments)) {
|
||||
return;
|
||||
if (runTool(arguments)) {
|
||||
qDebug("tls certificate generated");
|
||||
|
||||
return generateFingerprint(pathToUse);
|
||||
} else {
|
||||
qCritical("failed to generate tls certificate");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
generateFingerprint(pathToUse);
|
||||
emit info(tr("SSL certificate generated."));
|
||||
}
|
||||
bool TlsCertificate::generateFingerprint(const QString &certificateFilename) {
|
||||
qDebug("generating tls fingerprint");
|
||||
|
||||
emit generateFinished();
|
||||
}
|
||||
|
||||
void TlsCertificate::generateFingerprint(const QString &certificateFilename) {
|
||||
QStringList arguments;
|
||||
arguments.append("x509");
|
||||
arguments.append("-fingerprint");
|
||||
|
|
@ -203,23 +227,26 @@ void TlsCertificate::generateFingerprint(const QString &certificateFilename) {
|
|||
arguments.append(certificateFilename);
|
||||
|
||||
if (!runTool(arguments)) {
|
||||
return;
|
||||
qCritical("failed to generate tls fingerprint");
|
||||
return false;
|
||||
}
|
||||
|
||||
// find the fingerprint from the tool output
|
||||
auto i = m_ToolOutput.indexOf("=");
|
||||
auto i = m_toolStdout.indexOf("=");
|
||||
if (i != -1) {
|
||||
i++;
|
||||
QString fingerprint = m_ToolOutput.mid(i, m_ToolOutput.size() - i);
|
||||
QString fingerprint = m_toolStdout.mid(i, m_toolStdout.size() - i);
|
||||
|
||||
TlsFingerprint::local().trust(fingerprint, false);
|
||||
emit info(tr("SSL fingerprint generated."));
|
||||
qDebug("tls fingerprint generated");
|
||||
return true;
|
||||
} else {
|
||||
emit error(tr("Failed to find SSL fingerprint."));
|
||||
qCritical("failed to find tls fingerprint in tls tool output");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
QString TlsCertificate::getCertKeyLength(const QString &path) {
|
||||
int TlsCertificate::getCertKeyLength(const QString &path) {
|
||||
|
||||
QStringList arguments;
|
||||
arguments.append("rsa");
|
||||
|
|
@ -229,17 +256,19 @@ QString TlsCertificate::getCertKeyLength(const QString &path) {
|
|||
arguments.append("-noout");
|
||||
|
||||
if (!runTool(arguments)) {
|
||||
return QString();
|
||||
qFatal("failed to get key length from certificate");
|
||||
return 0;
|
||||
}
|
||||
|
||||
const QString searchStart("Private-Key: (");
|
||||
const QString searchEnd(" bit");
|
||||
|
||||
// Get the line that contains the key length from the output
|
||||
const auto indexStart = m_ToolOutput.indexOf(searchStart);
|
||||
const auto indexEnd = m_ToolOutput.indexOf(searchEnd, indexStart);
|
||||
const auto indexStart = m_toolStdout.indexOf(searchStart);
|
||||
const auto indexEnd = m_toolStdout.indexOf(searchEnd, indexStart);
|
||||
const auto start = indexStart + searchStart.length();
|
||||
const auto end = indexEnd - (indexStart + searchStart.length());
|
||||
auto keyLength = m_ToolOutput.mid(start, end);
|
||||
auto keyLength = m_toolStdout.mid(start, end);
|
||||
|
||||
return keyLength;
|
||||
return keyLength.toInt();
|
||||
}
|
||||
41
src/lib/gui/tls/TlsCertificate.h
Normal file
41
src/lib/gui/tls/TlsCertificate.h
Normal file
|
|
@ -0,0 +1,41 @@
|
|||
/*
|
||||
* synergy -- mouse and keyboard sharing utility
|
||||
* Copyright (C) 2015 Symless Ltd.
|
||||
*
|
||||
* This package is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* found in the file LICENSE that should have accompanied this file.
|
||||
*
|
||||
* This package is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "gui/core/CoreTool.h"
|
||||
|
||||
#include <QObject>
|
||||
|
||||
class TlsCertificate : public QObject {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit TlsCertificate(QObject *parent = nullptr);
|
||||
|
||||
bool generateCertificate(const QString &path, int keyLength);
|
||||
int getCertKeyLength(const QString &path);
|
||||
|
||||
private:
|
||||
bool runTool(const QStringList &args);
|
||||
bool generateFingerprint(const QString &certificateFilename);
|
||||
|
||||
private:
|
||||
QString m_profileDir;
|
||||
QString m_toolStdout;
|
||||
[[no_unique_address]] CoreTool m_coreTool;
|
||||
};
|
||||
|
|
@ -17,7 +17,7 @@
|
|||
|
||||
#include "TlsFingerprint.h"
|
||||
|
||||
#include "gui/CoreInterface.h"
|
||||
#include "gui/core/CoreTool.h"
|
||||
|
||||
#include <QDir>
|
||||
#include <QTextStream>
|
||||
|
|
@ -27,11 +27,10 @@ static const char kLocalFilename[] = "Local.txt";
|
|||
static const char kTrustedServersFilename[] = "TrustedServers.txt";
|
||||
static const char kTrustedClientsFilename[] = "TrustedClients.txt";
|
||||
|
||||
TlsFingerprint::TlsFingerprint(const QString &filename) {
|
||||
m_Filename = filename;
|
||||
}
|
||||
TlsFingerprint::TlsFingerprint(const QString &filename)
|
||||
: m_Filename(filename) {}
|
||||
|
||||
void TlsFingerprint::trust(const QString &fingerprintText, bool append) {
|
||||
void TlsFingerprint::trust(const QString &fingerprintText, bool append) const {
|
||||
TlsFingerprint::persistDirectory();
|
||||
|
||||
QIODevice::OpenMode openMode;
|
||||
|
|
@ -59,7 +58,7 @@ bool TlsFingerprint::fileExists() const {
|
|||
return file.exists();
|
||||
}
|
||||
|
||||
bool TlsFingerprint::isTrusted(const QString &fingerprintText) {
|
||||
bool TlsFingerprint::isTrusted(const QString &fingerprintText) const {
|
||||
QStringList list = readList();
|
||||
foreach (QString trusted, list) {
|
||||
if (trusted == fingerprintText) {
|
||||
|
|
@ -69,7 +68,7 @@ bool TlsFingerprint::isTrusted(const QString &fingerprintText) {
|
|||
return false;
|
||||
}
|
||||
|
||||
QStringList TlsFingerprint::readList(const int readTo) {
|
||||
QStringList TlsFingerprint::readList(const int readTo) const {
|
||||
QStringList list;
|
||||
|
||||
QString dirName = TlsFingerprint::directoryPath();
|
||||
|
|
@ -93,7 +92,7 @@ QStringList TlsFingerprint::readList(const int readTo) {
|
|||
return list;
|
||||
}
|
||||
|
||||
QString TlsFingerprint::readFirst() {
|
||||
QString TlsFingerprint::readFirst() const {
|
||||
QStringList list = readList(1);
|
||||
return list.at(0);
|
||||
}
|
||||
|
|
@ -111,8 +110,8 @@ void TlsFingerprint::persistDirectory() {
|
|||
}
|
||||
|
||||
QString TlsFingerprint::directoryPath() {
|
||||
CoreInterface coreInterface;
|
||||
QString profileDir = coreInterface.getProfileDir();
|
||||
CoreTool coreTool;
|
||||
QString profileDir = coreTool.getProfileDir();
|
||||
|
||||
return QString("%1/%2").arg(profileDir).arg(kDirName);
|
||||
}
|
||||
|
|
@ -21,15 +21,7 @@
|
|||
|
||||
class TlsFingerprint {
|
||||
private:
|
||||
TlsFingerprint(const QString &filename);
|
||||
|
||||
public:
|
||||
void trust(const QString &fingerprintText, bool append = true);
|
||||
bool isTrusted(const QString &fingerprintText);
|
||||
QStringList readList(const int readTo = -1);
|
||||
QString readFirst();
|
||||
QString filePath() const;
|
||||
bool fileExists() const;
|
||||
explicit TlsFingerprint(const QString &filename);
|
||||
|
||||
public:
|
||||
static TlsFingerprint local();
|
||||
|
|
@ -40,6 +32,13 @@ public:
|
|||
static bool localFingerprintExists();
|
||||
static void persistDirectory();
|
||||
|
||||
void trust(const QString &fingerprintText, bool append = true) const;
|
||||
bool isTrusted(const QString &fingerprintText) const;
|
||||
QStringList readList(const int readTo = -1) const;
|
||||
QString readFirst() const;
|
||||
QString filePath() const;
|
||||
bool fileExists() const;
|
||||
|
||||
private:
|
||||
QString m_Filename;
|
||||
};
|
||||
|
|
@ -20,10 +20,17 @@
|
|||
#include "TlsCertificate.h"
|
||||
#include "constants.h"
|
||||
|
||||
#include <QString>
|
||||
|
||||
namespace synergy::gui {
|
||||
|
||||
TlsUtility::TlsUtility(
|
||||
const IAppConfig &appConfig, const license::License &license)
|
||||
: m_appConfig(appConfig),
|
||||
m_license(license) {}
|
||||
|
||||
bool TlsUtility::isAvailable() const {
|
||||
return !kLicensingEnabled || m_license.isTlsAvailable();
|
||||
return !kEnableActivation || m_license.isTlsAvailable();
|
||||
}
|
||||
|
||||
bool TlsUtility::isAvailableAndEnabled() const {
|
||||
|
|
@ -31,20 +38,20 @@ bool TlsUtility::isAvailableAndEnabled() const {
|
|||
return isAvailable() && config.tlsEnabled();
|
||||
}
|
||||
|
||||
void TlsUtility::generateCertificate(bool replace) const {
|
||||
bool TlsUtility::generateCertificate() {
|
||||
qDebug("generating tls certificate, "
|
||||
"all clients must trust the new fingerprint");
|
||||
|
||||
if (!isAvailableAndEnabled()) {
|
||||
qFatal("unable to generate tls certificate, "
|
||||
qCritical("unable to generate tls certificate, "
|
||||
"tls is either not available or not enabled");
|
||||
return false;
|
||||
}
|
||||
|
||||
auto path = m_appConfig.tlsCertPath();
|
||||
auto length = m_appConfig.tlsKeyLength();
|
||||
|
||||
TlsCertificate cert;
|
||||
cert.generateCertificate(path, length, replace);
|
||||
return m_certificate.generateCertificate(path, length);
|
||||
}
|
||||
|
||||
} // namespace synergy::gui
|
||||
|
|
@ -17,18 +17,23 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include "IAppConfig.h"
|
||||
#include "gui/config/IAppConfig.h"
|
||||
|
||||
#include "TlsCertificate.h"
|
||||
#include "license/License.h"
|
||||
|
||||
#include <QObject>
|
||||
|
||||
namespace synergy::gui {
|
||||
|
||||
class TlsUtility {
|
||||
class TlsUtility : public QObject {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit TlsUtility(
|
||||
const IAppConfig &appConfig, const license::License &license)
|
||||
: m_appConfig(appConfig),
|
||||
m_license(license) {}
|
||||
const IAppConfig &appConfig, const license::License &license);
|
||||
|
||||
bool generateCertificate();
|
||||
|
||||
/**
|
||||
* @brief Combines the availability and the enabled status of TLS.
|
||||
|
|
@ -44,14 +49,10 @@ public:
|
|||
*/
|
||||
bool isAvailable() const;
|
||||
|
||||
/**
|
||||
* @param replace Replace certificate on disk (default: false).
|
||||
*/
|
||||
void generateCertificate(bool replace = false) const;
|
||||
|
||||
private:
|
||||
const IAppConfig &m_appConfig;
|
||||
const license::License &m_license;
|
||||
TlsCertificate m_certificate;
|
||||
};
|
||||
|
||||
} // namespace synergy::gui
|
||||
|
|
@ -56,7 +56,11 @@ Screen::~Screen() {
|
|||
disable();
|
||||
}
|
||||
assert(!m_enabled);
|
||||
|
||||
// TODO: why assert this? it appears to be false when an elevated dialog
|
||||
// appears on windows and the process is killed.
|
||||
assert(m_entered == m_isPrimary);
|
||||
|
||||
delete m_screen;
|
||||
LOG((CLOG_DEBUG "closed display"));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -15,14 +15,23 @@
|
|||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "gui/CommandProcess.h"
|
||||
#include "gui/core/CommandProcess.h"
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
TEST(CommandProcessTests, run_commandSucceeds_returnsOutput) {
|
||||
|
||||
// it seems that on windows, you can sometimes just call echo (this worked
|
||||
// with windows 2022), but on windows 10 you can't call echo directly.
|
||||
#if defined(Q_OS_WIN)
|
||||
const QString command = "cmd";
|
||||
const QStringList arguments = {"/C", "echo Hello, World!"};
|
||||
const QString input = "";
|
||||
#elif defined(Q_OS_UNIX)
|
||||
const QString command = "echo";
|
||||
const QStringList arguments = {"Hello, World!"};
|
||||
const QString input = "";
|
||||
#endif // Q_OS_WIN
|
||||
|
||||
CommandProcess commandProcess(command, arguments, input);
|
||||
|
||||
|
|
@ -15,41 +15,41 @@
|
|||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "gui/CoreInterface.h"
|
||||
#include "gui/core/CoreTool.h"
|
||||
#include "shared/gui/QtCoreTest.h"
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
class CoreInterfaceTests : public QtCoreTest {};
|
||||
class CoreToolTests : public QtCoreTest {};
|
||||
|
||||
TEST_F(CoreInterfaceTests, getProfileDir_noMock_returnsNonEmpty) {
|
||||
CoreInterface coreInterface;
|
||||
TEST_F(CoreToolTests, getProfileDir_noMock_returnsNonEmpty) {
|
||||
CoreTool coreTool;
|
||||
|
||||
QString profileDir = coreInterface.getProfileDir();
|
||||
QString profileDir = coreTool.getProfileDir();
|
||||
|
||||
EXPECT_FALSE(profileDir.isEmpty());
|
||||
}
|
||||
|
||||
TEST_F(CoreInterfaceTests, getInstalledDir_noMock_returnsNonEmpty) {
|
||||
CoreInterface coreInterface;
|
||||
TEST_F(CoreToolTests, getInstalledDir_noMock_returnsNonEmpty) {
|
||||
CoreTool coreTool;
|
||||
|
||||
QString installedDir = coreInterface.getInstalledDir();
|
||||
QString installedDir = coreTool.getInstalledDir();
|
||||
|
||||
EXPECT_FALSE(installedDir.isEmpty());
|
||||
}
|
||||
|
||||
TEST_F(CoreInterfaceTests, getArch_noMock_returnsNonEmpty) {
|
||||
CoreInterface coreInterface;
|
||||
TEST_F(CoreToolTests, getArch_noMock_returnsNonEmpty) {
|
||||
CoreTool coreTool;
|
||||
|
||||
QString arch = coreInterface.getArch();
|
||||
QString arch = coreTool.getArch();
|
||||
|
||||
EXPECT_FALSE(arch.isEmpty());
|
||||
}
|
||||
|
||||
TEST_F(CoreInterfaceTests, getSerialKeyFilePath_noMock_returnsNonEmpty) {
|
||||
CoreInterface coreInterface;
|
||||
TEST_F(CoreToolTests, getSerialKeyFilePath_noMock_returnsNonEmpty) {
|
||||
CoreTool coreTool;
|
||||
|
||||
QString serialKeyFilePath = coreInterface.getSerialKeyFilePath();
|
||||
QString serialKeyFilePath = coreTool.getSerialKeyFilePath();
|
||||
|
||||
EXPECT_FALSE(serialKeyFilePath.isEmpty());
|
||||
}
|
||||
|
|
@ -33,7 +33,7 @@ void TestEventQueue::initQuitTimeout(double timeout) {
|
|||
|
||||
void TestEventQueue::cleanupQuitTimeout() {
|
||||
removeHandler(Event::kTimer, m_pQuitTimeoutTimer);
|
||||
delete m_pQuitTimeoutTimer;
|
||||
deleteTimer(m_pQuitTimeoutTimer);
|
||||
m_pQuitTimeoutTimer = nullptr;
|
||||
}
|
||||
|
||||
|
|
|
|||
40
src/test/unittests/gui/byte_utils_tests.cpp
Normal file
40
src/test/unittests/gui/byte_utils_tests.cpp
Normal file
|
|
@ -0,0 +1,40 @@
|
|||
/*
|
||||
* synergy -- mouse and keyboard sharing utility
|
||||
* Copyright (C) 2024 Symless Ltd.
|
||||
*
|
||||
* This package is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* found in the file LICENSE that should have accompanied this file.
|
||||
*
|
||||
* This package is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "gui/byte_utils.h"
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
using namespace synergy::gui;
|
||||
|
||||
TEST(byte_utils_tests, bytesToInt_size4) {
|
||||
char buffer[4] = {0x01, 0x02, 0x03, 0x04};
|
||||
|
||||
const auto i = bytesToInt(buffer, 4);
|
||||
|
||||
EXPECT_EQ(i, 0x01020304);
|
||||
}
|
||||
|
||||
TEST(byte_utils_tests, intToBytes_size4) {
|
||||
QByteArray bytes = intToBytes(0x01020304);
|
||||
|
||||
EXPECT_EQ(bytes.size(), 4);
|
||||
EXPECT_EQ(bytes[0], 0x01);
|
||||
EXPECT_EQ(bytes[1], 0x02);
|
||||
EXPECT_EQ(bytes[2], 0x03);
|
||||
EXPECT_EQ(bytes[3], 0x04);
|
||||
}
|
||||
|
|
@ -15,7 +15,7 @@
|
|||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "gui/AppConfig.h"
|
||||
#include "gui/config/AppConfig.h"
|
||||
|
||||
#include "gmock/gmock.h"
|
||||
#include <gmock/gmock.h>
|
||||
231
src/test/unittests/gui/core/CoreProcessTests.cpp
Normal file
231
src/test/unittests/gui/core/CoreProcessTests.cpp
Normal file
|
|
@ -0,0 +1,231 @@
|
|||
/*
|
||||
* synergy -- mouse and keyboard sharing utility
|
||||
* Copyright (C) 2024 Symless Ltd.
|
||||
*
|
||||
* This package is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* found in the file LICENSE that should have accompanied this file.
|
||||
*
|
||||
* This package is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "gui/config/IAppConfig.h"
|
||||
#include "gui/core/CoreProcess.h"
|
||||
#include "gui/ipc/IQIpcClient.h"
|
||||
#include "gui/proxy/QProcessProxy.h"
|
||||
|
||||
#include "gmock/gmock.h"
|
||||
#include <gmock/gmock.h>
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
using namespace synergy::gui;
|
||||
using ::testing::_;
|
||||
using ::testing::NiceMock;
|
||||
using ::testing::Return;
|
||||
using ::testing::ReturnRef;
|
||||
|
||||
class MockAppConfig : public IAppConfig {
|
||||
public:
|
||||
MockAppConfig() {
|
||||
ON_CALL(*this, screenName()).WillByDefault(ReturnRef(m_stubName));
|
||||
ON_CALL(*this, networkInterface())
|
||||
.WillByDefault(ReturnRef(m_stubInterface));
|
||||
ON_CALL(*this, logLevelText()).WillByDefault(Return("stub log level"));
|
||||
}
|
||||
|
||||
MOCK_METHOD(QString, tlsCertPath, (), (const, override));
|
||||
MOCK_METHOD(int, tlsKeyLength, (), (const, override));
|
||||
MOCK_METHOD(bool, tlsEnabled, (), (const, override));
|
||||
MOCK_METHOD(ProcessMode, processMode, (), (const, override));
|
||||
MOCK_METHOD(ElevateMode, elevateMode, (), (const, override));
|
||||
MOCK_METHOD(QString, logLevelText, (), (const, override));
|
||||
MOCK_METHOD(const QString &, screenName, (), (const, override));
|
||||
MOCK_METHOD(bool, preventSleep, (), (const, override));
|
||||
MOCK_METHOD(bool, logToFile, (), (const, override));
|
||||
MOCK_METHOD(const QString &, logFilename, (), (const, override));
|
||||
MOCK_METHOD(QString, coreServerName, (), (const, override));
|
||||
MOCK_METHOD(QString, coreClientName, (), (const, override));
|
||||
MOCK_METHOD(bool, invertConnection, (), (const, override));
|
||||
MOCK_METHOD(void, persistLogDir, (), (const, override));
|
||||
MOCK_METHOD(QString, serialKey, (), (const, override));
|
||||
MOCK_METHOD(bool, languageSync, (), (const, override));
|
||||
MOCK_METHOD(bool, invertScrollDirection, (), (const, override));
|
||||
MOCK_METHOD(int, port, (), (const, override));
|
||||
MOCK_METHOD(bool, useExternalConfig, (), (const, override));
|
||||
MOCK_METHOD(const QString &, configFile, (), (const, override));
|
||||
MOCK_METHOD(const QString &, networkInterface, (), (const, override));
|
||||
|
||||
private:
|
||||
const QString m_stubName = "stub name";
|
||||
const QString m_stubInterface = "stub interface";
|
||||
const QString m_stubAddress = "stub address";
|
||||
};
|
||||
|
||||
class MockServerConfig : public IServerConfig {
|
||||
public:
|
||||
MOCK_METHOD(bool, isFull, (), (const, override));
|
||||
MOCK_METHOD(
|
||||
bool, screenExists, (const QString &screenName), (const, override));
|
||||
MOCK_METHOD(bool, save, (const QString &fileName), (const, override));
|
||||
MOCK_METHOD(void, save, (QFile & file), (const, override));
|
||||
MOCK_METHOD(bool, enableDragAndDrop, (), (const, override));
|
||||
};
|
||||
|
||||
class MockQProcessProxy : public proxy::QProcessProxy {
|
||||
public:
|
||||
operator bool() const override { return toBool(); }
|
||||
|
||||
MockQProcessProxy() {
|
||||
ON_CALL(*this, toBool()).WillByDefault(Return(true));
|
||||
ON_CALL(*this, state())
|
||||
.WillByDefault(Return(QProcess::ProcessState::Running));
|
||||
ON_CALL(*this, waitForStarted()).WillByDefault(Return(true));
|
||||
}
|
||||
|
||||
MOCK_METHOD(bool, toBool, (), (const));
|
||||
MOCK_METHOD(void, create, (), (override));
|
||||
MOCK_METHOD(
|
||||
void, start, (const QString &program, const QStringList &arguments),
|
||||
(override));
|
||||
MOCK_METHOD(bool, waitForStarted, (), (override));
|
||||
MOCK_METHOD(QProcess::ProcessState, state, (), (const, override));
|
||||
MOCK_METHOD(void, close, (), (override));
|
||||
MOCK_METHOD(QString, readAllStandardOutput, (), (override));
|
||||
MOCK_METHOD(QString, readAllStandardError, (), (override));
|
||||
};
|
||||
|
||||
class MockQIpcClient : public ipc::IQIpcClient {
|
||||
public:
|
||||
MockQIpcClient() {
|
||||
ON_CALL(*this, isConnected()).WillByDefault(Return(true));
|
||||
}
|
||||
|
||||
MOCK_METHOD(void, sendHello, (), (const, override));
|
||||
MOCK_METHOD(
|
||||
void, sendCommand, (const QString &command, ElevateMode elevate),
|
||||
(const, override));
|
||||
MOCK_METHOD(void, connectToHost, (), (override));
|
||||
MOCK_METHOD(void, disconnectFromHost, (), (override));
|
||||
MOCK_METHOD(bool, isConnected, (), (const, override));
|
||||
};
|
||||
|
||||
class MockDeps : public CoreProcess::Deps {
|
||||
public:
|
||||
MockDeps() {
|
||||
ON_CALL(*this, process()).WillByDefault(ReturnRef(m_process));
|
||||
ON_CALL(*this, ipcClient()).WillByDefault(ReturnRef(m_ipcClient));
|
||||
ON_CALL(*this, appPath(_)).WillByDefault(Return("stub app path"));
|
||||
ON_CALL(*this, fileExists(_)).WillByDefault(Return(true));
|
||||
ON_CALL(*this, getProfileRoot()).WillByDefault(Return("stub profile"));
|
||||
}
|
||||
|
||||
MOCK_METHOD(proxy::QProcessProxy &, process, (), (override));
|
||||
MOCK_METHOD(ipc::IQIpcClient &, ipcClient, (), (override));
|
||||
MOCK_METHOD(QString, appPath, (const QString &name), (const, override));
|
||||
MOCK_METHOD(bool, fileExists, (const QString &path), (const, override));
|
||||
MOCK_METHOD(QString, getProfileRoot, (), (const, override));
|
||||
|
||||
NiceMock<MockQProcessProxy> m_process;
|
||||
NiceMock<MockQIpcClient> m_ipcClient;
|
||||
};
|
||||
|
||||
class CoreProcessTests : public ::testing::Test {
|
||||
public:
|
||||
CoreProcessTests() : m_coreProcess(m_appConfig, m_serverConfig, m_pDeps) {}
|
||||
|
||||
NiceMock<MockAppConfig> m_appConfig;
|
||||
NiceMock<MockServerConfig> m_serverConfig;
|
||||
std::shared_ptr<NiceMock<MockDeps>> m_pDeps =
|
||||
std::make_shared<NiceMock<MockDeps>>();
|
||||
CoreProcess m_coreProcess;
|
||||
};
|
||||
|
||||
TEST_F(CoreProcessTests, start_serverDesktop_callsProcessStart) {
|
||||
m_coreProcess.setMode(CoreProcess::Mode::Server);
|
||||
|
||||
EXPECT_CALL(m_pDeps->m_process, start(_, _)).Times(1);
|
||||
|
||||
m_coreProcess.start(ProcessMode::kDesktop);
|
||||
}
|
||||
|
||||
TEST_F(CoreProcessTests, start_serverService_callsSendCommand) {
|
||||
m_coreProcess.setMode(CoreProcess::Mode::Server);
|
||||
|
||||
EXPECT_CALL(m_pDeps->m_ipcClient, sendCommand(_, _)).Times(1);
|
||||
|
||||
m_coreProcess.start(ProcessMode::kService);
|
||||
}
|
||||
|
||||
TEST_F(CoreProcessTests, start_clientDesktop_callsProcessStart) {
|
||||
m_coreProcess.setMode(CoreProcess::Mode::Client);
|
||||
m_coreProcess.setAddress("stub address");
|
||||
|
||||
EXPECT_CALL(m_pDeps->m_process, start(_, _)).Times(1);
|
||||
|
||||
m_coreProcess.start(ProcessMode::kDesktop);
|
||||
}
|
||||
|
||||
TEST_F(CoreProcessTests, start_clientService_callsSendCommand) {
|
||||
m_coreProcess.setMode(CoreProcess::Mode::Client);
|
||||
m_coreProcess.setAddress("stub address");
|
||||
|
||||
EXPECT_CALL(m_pDeps->m_ipcClient, sendCommand(_, _)).Times(1);
|
||||
|
||||
m_coreProcess.start(ProcessMode::kService);
|
||||
}
|
||||
|
||||
TEST_F(CoreProcessTests, stop_serverDesktop_callsProcessClose) {
|
||||
m_coreProcess.setMode(CoreProcess::Mode::Server);
|
||||
m_coreProcess.start();
|
||||
|
||||
EXPECT_CALL(m_pDeps->m_process, close()).Times(1);
|
||||
|
||||
m_coreProcess.stop(ProcessMode::kDesktop);
|
||||
}
|
||||
|
||||
TEST_F(CoreProcessTests, stop_serverService_callsSendCommand) {
|
||||
m_coreProcess.setMode(CoreProcess::Mode::Server);
|
||||
m_coreProcess.start();
|
||||
|
||||
EXPECT_CALL(m_pDeps->m_ipcClient, sendCommand(_, _)).Times(1);
|
||||
|
||||
m_coreProcess.stop(ProcessMode::kService);
|
||||
}
|
||||
|
||||
TEST_F(CoreProcessTests, stop_clientDesktop_callsProcessClose) {
|
||||
m_coreProcess.setMode(CoreProcess::Mode::Client);
|
||||
m_coreProcess.setAddress("stub address");
|
||||
m_coreProcess.start();
|
||||
|
||||
EXPECT_CALL(m_pDeps->m_process, close()).Times(1);
|
||||
|
||||
m_coreProcess.stop(ProcessMode::kDesktop);
|
||||
}
|
||||
|
||||
TEST_F(CoreProcessTests, stop_clientService_callsSendCommand) {
|
||||
m_coreProcess.setMode(CoreProcess::Mode::Client);
|
||||
m_coreProcess.setAddress("stub address");
|
||||
m_coreProcess.start();
|
||||
|
||||
EXPECT_CALL(m_pDeps->m_ipcClient, sendCommand(_, _)).Times(1);
|
||||
|
||||
m_coreProcess.stop(ProcessMode::kService);
|
||||
}
|
||||
|
||||
TEST_F(CoreProcessTests, restart_serverDesktop_callsProcessStart) {
|
||||
ON_CALL(m_appConfig, processMode())
|
||||
.WillByDefault(Return(ProcessMode::kDesktop));
|
||||
m_coreProcess.setMode(CoreProcess::Mode::Server);
|
||||
m_coreProcess.start();
|
||||
|
||||
EXPECT_CALL(m_pDeps->m_process, close()).Times(1);
|
||||
EXPECT_CALL(m_pDeps->m_process, start(_, _)).Times(1);
|
||||
|
||||
m_coreProcess.restart();
|
||||
}
|
||||
|
|
@ -15,7 +15,7 @@
|
|||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "gui/QIpcClient.h"
|
||||
#include "gui/ipc/QIpcClient.h"
|
||||
|
||||
#include <gmock/gmock.h>
|
||||
#include <gtest/gtest.h>
|
||||
|
|
@ -25,7 +25,7 @@ using testing::StrEq;
|
|||
|
||||
class MockStream : public QDataStreamProxy {
|
||||
public:
|
||||
MOCK_METHOD(qint64, writeRawData, (const char *, qint64), (override));
|
||||
MOCK_METHOD(qint64, writeRawData, (const char *, int), (override));
|
||||
};
|
||||
|
||||
TEST(QIpcClientTests, sendCommand_anyCommand_commandSent) {
|
||||
|
|
@ -38,5 +38,5 @@ TEST(QIpcClientTests, sendCommand_anyCommand_commandSent) {
|
|||
EXPECT_CALL(*mockStream, writeRawData(StrEq("test"), 4)).Times(1);
|
||||
|
||||
QIpcClient ipcClient(streamProvider);
|
||||
ipcClient.sendCommand("test", ElevateMode::ElevateAsNeeded);
|
||||
ipcClient.sendCommand("test", ElevateMode::kAutomatic);
|
||||
}
|
||||
|
|
@ -15,7 +15,7 @@
|
|||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "gui/LicenseHandler.h"
|
||||
#include "gui/license/LicenseHandler.h"
|
||||
|
||||
#include <chrono>
|
||||
#include <gtest/gtest.h>
|
||||
|
|
@ -15,7 +15,7 @@
|
|||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "gui/license_notices.h"
|
||||
#include "gui/license/license_notices.h"
|
||||
|
||||
#include <chrono>
|
||||
#include <gmock/gmock.h>
|
||||
Loading…
Reference in a new issue