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)
|
# Version checker URL to use (useful for testing)
|
||||||
# SYNERGY_VERSION_URL="http://localhost:8787?version=v1"
|
# 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
|
# 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_VERSION: ${{ github.event.inputs.version || github.event.release.tag_name }}
|
||||||
SYNERGY_PRODUCT_NAME: ${{ vars.SYNERGY_PRODUCT_NAME }}
|
SYNERGY_PRODUCT_NAME: ${{ vars.SYNERGY_PRODUCT_NAME }}
|
||||||
SYNERGY_PACKAGE_PREFIX: ${{ vars.SYNERGY_PACKAGE_PREFIX }}
|
SYNERGY_PACKAGE_PREFIX: ${{ vars.SYNERGY_PACKAGE_PREFIX }}
|
||||||
SYNERGY_ENABLE_LICENSING: ${{ vars.SYNERGY_ENABLE_LICENSING }}
|
SYNERGY_LICENSED_PRODUCT: ${{ vars.SYNERGY_LICENSED_PRODUCT }}
|
||||||
SYNERGY_SHOW_DEV_THANKS: ${{ vars.SYNERGY_SHOW_DEV_THANKS }}
|
SYNERGY_ENABLE_ACTIVATION: ${{ vars.SYNERGY_ENABLE_ACTIVATION }}
|
||||||
PACKAGE_BUILD: ${{ !github.event.pull_request.draft }}
|
PACKAGE_BUILD: ${{ !github.event.pull_request.draft }}
|
||||||
PACKAGE_UPLOAD: ${{ !github.event.pull_request.draft && github.event_name != 'schedule' }}
|
PACKAGE_UPLOAD: ${{ !github.event.pull_request.draft && github.event_name != 'schedule' }}
|
||||||
UPLOAD_TO_GITHUB: ${{ github.event_name == 'pull_request' && !github.event.pull_request.draft }}
|
UPLOAD_TO_GITHUB: ${{ github.event_name == 'pull_request' && !github.event.pull_request.draft }}
|
||||||
|
|
|
||||||
4
.vscode/launch.json
vendored
4
.vscode/launch.json
vendored
|
|
@ -11,7 +11,7 @@
|
||||||
{
|
{
|
||||||
"name": "gui windows",
|
"name": "gui windows",
|
||||||
"type": "cppvsdbg",
|
"type": "cppvsdbg",
|
||||||
"cwd": "${workspaceRoot}/build/bin",
|
"cwd": "${workspaceRoot}",
|
||||||
"request": "launch",
|
"request": "launch",
|
||||||
"program": "${workspaceFolder}/build/bin/synergy",
|
"program": "${workspaceFolder}/build/bin/synergy",
|
||||||
"internalConsoleOptions": "openOnSessionStart",
|
"internalConsoleOptions": "openOnSessionStart",
|
||||||
|
|
@ -60,7 +60,7 @@
|
||||||
"preLaunchTask": "build"
|
"preLaunchTask": "build"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "daemon windows attach",
|
"name": "windows attach",
|
||||||
"type": "cppvsdbg",
|
"type": "cppvsdbg",
|
||||||
"request": "attach",
|
"request": "attach",
|
||||||
"processId": "${command:pickProcess}"
|
"processId": "${command:pickProcess}"
|
||||||
|
|
|
||||||
|
|
@ -68,6 +68,7 @@ Enhancements:
|
||||||
- #7422 Handle empty value for computer name setting
|
- #7422 Handle empty value for computer name setting
|
||||||
- #7423 Refactor local/global save/load and show dev thanks message
|
- #7423 Refactor local/global save/load and show dev thanks message
|
||||||
- #7424 Add Alma Linux and Rocky Linux runners
|
- #7424 Add Alma Linux and Rocky Linux runners
|
||||||
|
- #7425 Refactored core process management and IPC client
|
||||||
|
|
||||||
# 1.14.6
|
# 1.14.6
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -45,14 +45,20 @@ macro(configure_definitions)
|
||||||
add_definitions(-DGIT_SHA_SHORT="${GIT_SHA_SHORT}")
|
add_definitions(-DGIT_SHA_SHORT="${GIT_SHA_SHORT}")
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if(ENABLE_LICENSING)
|
if(NOT "$ENV{SYNERGY_PRODUCT_NAME}" STREQUAL "")
|
||||||
message(STATUS "Licensing enabled")
|
set(PRODUCT_NAME $ENV{SYNERGY_PRODUCT_NAME})
|
||||||
add_definitions(-DSYNERGY_ENABLE_LICENSING=1)
|
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()
|
else()
|
||||||
set(PRODUCT_NAME "Synergy 1 Community Edition")
|
set(PRODUCT_NAME "Synergy 1 Community Edition")
|
||||||
if(NOT "$ENV{SYNERGY_PRODUCT_NAME}" STREQUAL "")
|
|
||||||
set(PRODUCT_NAME $ENV{SYNERGY_PRODUCT_NAME})
|
|
||||||
endif()
|
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if(PRODUCT_NAME)
|
if(PRODUCT_NAME)
|
||||||
|
|
@ -110,10 +116,13 @@ macro(configure_options)
|
||||||
# coverage is off by default because it's GCC only and a developer preference.
|
# coverage is off by default because it's GCC only and a developer preference.
|
||||||
set(DEFAULT_ENABLE_COVERAGE OFF)
|
set(DEFAULT_ENABLE_COVERAGE OFF)
|
||||||
|
|
||||||
# licensing is off by default to make life easier for contributors.
|
# licensed product is off by default to show links to github, etc.
|
||||||
set(DEFAULT_ENABLE_LICENSING OFF)
|
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)
|
set(DEFAULT_SHOW_DEV_THANKS ON)
|
||||||
|
|
||||||
if("$ENV{SYNERGY_BUILD_MINIMAL}" STREQUAL "true")
|
if("$ENV{SYNERGY_BUILD_MINIMAL}" STREQUAL "true")
|
||||||
|
|
@ -129,8 +138,12 @@ macro(configure_options)
|
||||||
set(DEFAULT_BUILD_UNIFIED ON)
|
set(DEFAULT_BUILD_UNIFIED ON)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if("$ENV{SYNERGY_ENABLE_LICENSING}" STREQUAL "true")
|
if("$ENV{SYNERGY_ENABLE_ACTIVATION}" STREQUAL "true")
|
||||||
set(DEFAULT_ENABLE_LICENSING ON)
|
set(DEFAULT_ENABLE_ACTIVATION ON)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
if("$ENV{SYNERGY_LICENSED_PRODUCT}" STREQUAL "true")
|
||||||
|
set(DEFAULT_LICENSED_PRODUCT ON)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if("$ENV{SYNERGY_ENABLE_COVERAGE}" STREQUAL "true")
|
if("$ENV{SYNERGY_ENABLE_COVERAGE}" STREQUAL "true")
|
||||||
|
|
@ -145,7 +158,8 @@ macro(configure_options)
|
||||||
option(BUILD_INSTALLER "Build installer" ${DEFAULT_BUILD_INSTALLER})
|
option(BUILD_INSTALLER "Build installer" ${DEFAULT_BUILD_INSTALLER})
|
||||||
option(BUILD_TESTS "Build tests" ${DEFAULT_BUILD_TESTS})
|
option(BUILD_TESTS "Build tests" ${DEFAULT_BUILD_TESTS})
|
||||||
option(BUILD_UNIFIED "Build unified binary" ${DEFAULT_BUILD_UNIFIED})
|
option(BUILD_UNIFIED "Build unified binary" ${DEFAULT_BUILD_UNIFIED})
|
||||||
option(ENABLE_LICENSING "Enable licensing" ${DEFAULT_ENABLE_LICENSING})
|
option(ENABLE_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(ENABLE_COVERAGE "Enable test coverage" ${DEFAULT_ENABLE_COVERAGE})
|
||||||
option(SHOW_DEV_THANKS "Show dev thanks message" ${DEFAULT_SHOW_DEV_THANKS})
|
option(SHOW_DEV_THANKS "Show dev thanks message" ${DEFAULT_SHOW_DEV_THANKS})
|
||||||
|
|
||||||
|
|
|
||||||
25
cspell.json
25
cspell.json
|
|
@ -4,23 +4,46 @@
|
||||||
"dictionaryDefinitions": [],
|
"dictionaryDefinitions": [],
|
||||||
"dictionaries": [],
|
"dictionaries": [],
|
||||||
"words": [
|
"words": [
|
||||||
"Schoeneman",
|
|
||||||
"aqtinstall",
|
"aqtinstall",
|
||||||
|
"Axelson",
|
||||||
|
"Breen",
|
||||||
"codesign",
|
"codesign",
|
||||||
"codesigning",
|
"codesigning",
|
||||||
"contribs",
|
"contribs",
|
||||||
|
"Daun",
|
||||||
"distros",
|
"distros",
|
||||||
"dmgbuild",
|
"dmgbuild",
|
||||||
"dotenv",
|
"dotenv",
|
||||||
|
"Evenson",
|
||||||
|
"Feder",
|
||||||
"gdrive",
|
"gdrive",
|
||||||
|
"Hadzhylov",
|
||||||
|
"Hetu",
|
||||||
"keychain",
|
"keychain",
|
||||||
"Keychains",
|
"Keychains",
|
||||||
|
"Kutytska",
|
||||||
|
"Lanz",
|
||||||
"LLDB",
|
"LLDB",
|
||||||
|
"Lysytsia",
|
||||||
"macdeployqt",
|
"macdeployqt",
|
||||||
"msvc",
|
"msvc",
|
||||||
|
"noquote",
|
||||||
"notarytool",
|
"notarytool",
|
||||||
|
"Oleksandr",
|
||||||
|
"Olena",
|
||||||
"outputdir",
|
"outputdir",
|
||||||
|
"Petroules",
|
||||||
|
"Poschta",
|
||||||
|
"Priddy",
|
||||||
"runas",
|
"runas",
|
||||||
|
"Sbârnea",
|
||||||
|
"Schoeneman",
|
||||||
|
"Serhii",
|
||||||
|
"Sorin",
|
||||||
|
"synergyc",
|
||||||
|
"synergyd",
|
||||||
|
"synergys",
|
||||||
|
"Volker",
|
||||||
"winget"
|
"winget"
|
||||||
],
|
],
|
||||||
"ignoreWords": [],
|
"ignoreWords": [],
|
||||||
|
|
|
||||||
|
|
@ -133,9 +133,12 @@ class Dependencies:
|
||||||
print("Running dependencies prerequisites command")
|
print("Running dependencies prerequisites command")
|
||||||
|
|
||||||
check = True
|
check = True
|
||||||
if distro_like and "fedora" in distro_like:
|
if distro == "fedora" or (distro_like and "fedora" in distro_like):
|
||||||
print("Fedora-like detected, ignoring return code")
|
print(
|
||||||
# On Fedora, dnf update returns code 100 when updates are available.
|
"Fedora-like detected, "
|
||||||
|
"ignoring return code on dependencies prerequisites command"
|
||||||
|
)
|
||||||
|
# On Fedora-like, dnf update returns code 100 when updates are available.
|
||||||
check = False
|
check = False
|
||||||
|
|
||||||
linux.run_command(command_pre, check)
|
linux.run_command(command_pre, check)
|
||||||
|
|
|
||||||
|
|
@ -29,11 +29,6 @@ file(
|
||||||
src/widgets/*)
|
src/widgets/*)
|
||||||
file(GLOB GUI_UI_FILES src/*.ui)
|
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)
|
if(WIN32)
|
||||||
set(GUI_RC_FILES res/win/Synergy.rc ${CMAKE_BINARY_DIR}/src/version.rc)
|
set(GUI_RC_FILES res/win/Synergy.rc ${CMAKE_BINARY_DIR}/src/version.rc)
|
||||||
endif()
|
endif()
|
||||||
|
|
|
||||||
|
|
@ -21,7 +21,7 @@
|
||||||
#include <QDateTime>
|
#include <QDateTime>
|
||||||
|
|
||||||
#if defined(Q_OS_MAC)
|
#if defined(Q_OS_MAC)
|
||||||
#include "OSXHelpers.h"
|
#include "gui/OSXHelpers.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#include "gui/version.h"
|
#include "gui/version.h"
|
||||||
|
|
@ -29,8 +29,11 @@
|
||||||
AboutDialog::AboutDialog(MainWindow *parent)
|
AboutDialog::AboutDialog(MainWindow *parent)
|
||||||
: QDialog(parent, Qt::WindowTitleHint | Qt::WindowSystemMenuHint),
|
: QDialog(parent, Qt::WindowTitleHint | Qt::WindowSystemMenuHint),
|
||||||
Ui::AboutDialogBase() {
|
Ui::AboutDialogBase() {
|
||||||
|
|
||||||
setupUi(this);
|
setupUi(this);
|
||||||
|
|
||||||
|
this->setFixedSize(this->size());
|
||||||
|
|
||||||
m_pLabelSynergyVersion->setText(synergy::gui::version());
|
m_pLabelSynergyVersion->setText(synergy::gui::version());
|
||||||
|
|
||||||
QString buildDateString = QString::fromLocal8Bit(__DATE__).simplified();
|
QString buildDateString = QString::fromLocal8Bit(__DATE__).simplified();
|
||||||
|
|
@ -59,21 +62,47 @@ void AboutDialog::updateLogo() const {
|
||||||
}
|
}
|
||||||
|
|
||||||
QString AboutDialog::getImportantDevelopers() const {
|
QString AboutDialog::getImportantDevelopers() const {
|
||||||
return QString(
|
QStringList awesomePeople;
|
||||||
// The ultimate creator
|
|
||||||
"Chris Schoeneman, "
|
|
||||||
|
|
||||||
// Precursor developers
|
// Chris is the ultimate creator, and the one who started it all in 2001.
|
||||||
"Richard Lee, Adam Feder, "
|
awesomePeople << "Chris Schoeneman";
|
||||||
|
|
||||||
// Contributors
|
// Richard and Adam developed CosmoSynergy, the 90's predecessor project.
|
||||||
"Nick Bolton, Volker Lanz, Ryan Breen, Guido Poschta, "
|
awesomePeople << "Richard Lee" << "Adam Feder";
|
||||||
"Bertrand Landry Hetu, Tom Chadwick, Brent Priddy, Jason Axelson, "
|
|
||||||
"Jake Petroules, Sorin Sbârnea, "
|
|
||||||
|
|
||||||
// Symless employees
|
// Nick continued the legacy in 2009 started by Chris.
|
||||||
"Kyle Bloom, Daun Chung, Serhii Hadzhylov, "
|
awesomePeople << "Nick Bolton";
|
||||||
"Oleksandr Lysytsia, Olena Kutytska.");
|
|
||||||
|
// 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 {
|
QString AboutDialog::getCopyright() const {
|
||||||
|
|
|
||||||
|
|
@ -12,28 +12,10 @@
|
||||||
<rect>
|
<rect>
|
||||||
<x>0</x>
|
<x>0</x>
|
||||||
<y>0</y>
|
<y>0</y>
|
||||||
<width>450</width>
|
<width>600</width>
|
||||||
<height>495</height>
|
<height>400</height>
|
||||||
</rect>
|
</rect>
|
||||||
</property>
|
</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">
|
<property name="windowTitle">
|
||||||
<string>About Synergy</string>
|
<string>About Synergy</string>
|
||||||
</property>
|
</property>
|
||||||
|
|
@ -90,7 +72,6 @@
|
||||||
<widget class="QLabel" name="m_pTitleLabel">
|
<widget class="QLabel" name="m_pTitleLabel">
|
||||||
<property name="font">
|
<property name="font">
|
||||||
<font>
|
<font>
|
||||||
<weight>75</weight>
|
|
||||||
<bold>true</bold>
|
<bold>true</bold>
|
||||||
</font>
|
</font>
|
||||||
</property>
|
</property>
|
||||||
|
|
@ -161,6 +142,9 @@
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>Developers</string>
|
<string>Developers</string>
|
||||||
</property>
|
</property>
|
||||||
|
<property name="textFormat">
|
||||||
|
<enum>Qt::RichText</enum>
|
||||||
|
</property>
|
||||||
<property name="wordWrap">
|
<property name="wordWrap">
|
||||||
<bool>true</bool>
|
<bool>true</bool>
|
||||||
</property>
|
</property>
|
||||||
|
|
|
||||||
|
|
@ -19,10 +19,10 @@
|
||||||
|
|
||||||
#include "CancelActivationDialog.h"
|
#include "CancelActivationDialog.h"
|
||||||
#include "MainWindow.h"
|
#include "MainWindow.h"
|
||||||
#include "gui/AppConfig.h"
|
#include "gui/config/AppConfig.h"
|
||||||
#include "gui/LicenseHandler.h"
|
|
||||||
#include "gui/constants.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 "gui/styles.h"
|
||||||
#include "license/ProductEdition.h"
|
#include "license/ProductEdition.h"
|
||||||
#include "license/parse_serial_key.h"
|
#include "license/parse_serial_key.h"
|
||||||
|
|
|
||||||
|
|
@ -17,7 +17,7 @@
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "gui/LicenseHandler.h"
|
#include "gui/license/LicenseHandler.h"
|
||||||
|
|
||||||
#include <QDialog>
|
#include <QDialog>
|
||||||
|
|
||||||
|
|
@ -42,8 +42,8 @@ public:
|
||||||
};
|
};
|
||||||
|
|
||||||
public slots:
|
public slots:
|
||||||
void reject();
|
void reject() override;
|
||||||
void accept();
|
void accept() override;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
void refreshSerialKey();
|
void refreshSerialKey();
|
||||||
|
|
|
||||||
File diff suppressed because it is too large
Load diff
|
|
@ -24,21 +24,19 @@
|
||||||
#include <QSettings>
|
#include <QSettings>
|
||||||
#include <QSystemTrayIcon>
|
#include <QSystemTrayIcon>
|
||||||
#include <QThread>
|
#include <QThread>
|
||||||
#include <memory>
|
|
||||||
|
|
||||||
#include "ui_MainWindowBase.h"
|
|
||||||
|
|
||||||
#include "ActivationDialog.h"
|
#include "ActivationDialog.h"
|
||||||
#include "ClientConnection.h"
|
|
||||||
#include "ServerConfig.h"
|
#include "ServerConfig.h"
|
||||||
#include "ServerConnection.h"
|
|
||||||
#include "TrayIcon.h"
|
|
||||||
#include "global/Ipc.h"
|
#include "global/Ipc.h"
|
||||||
#include "gui/AppConfig.h"
|
#include "gui/TrayIcon.h"
|
||||||
#include "gui/ConfigScopes.h"
|
|
||||||
#include "gui/QIpcClient.h"
|
|
||||||
#include "gui/TlsUtility.h"
|
|
||||||
#include "gui/VersionChecker.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 QAction;
|
||||||
class QMenu;
|
class QMenu;
|
||||||
|
|
@ -53,35 +51,22 @@ class QRadioButton;
|
||||||
class QMessageBox;
|
class QMessageBox;
|
||||||
class QAbstractButton;
|
class QAbstractButton;
|
||||||
|
|
||||||
class LogDialog;
|
|
||||||
class QSynergyApplication;
|
class QSynergyApplication;
|
||||||
class SetupWizard;
|
class SetupWizard;
|
||||||
class DataDownloader;
|
|
||||||
class CommandProcess;
|
|
||||||
class TlsCertificate;
|
|
||||||
|
|
||||||
class MainWindow : public QMainWindow, public Ui::MainWindowBase {
|
class MainWindow : public QMainWindow, public Ui::MainWindowBase {
|
||||||
|
using CoreMode = synergy::gui::CoreProcess::Mode;
|
||||||
|
using CoreProcess = synergy::gui::CoreProcess;
|
||||||
|
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
friend class QSynergyApplication;
|
friend class QSynergyApplication;
|
||||||
friend class SetupWizard;
|
friend class SetupWizard;
|
||||||
friend class ActivationDialog;
|
friend class ActivationDialog;
|
||||||
friend class SettingsDialog;
|
friend class SettingsDialog;
|
||||||
friend class ServerConnection;
|
|
||||||
friend class ClientConnection;
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
enum class CoreState {
|
|
||||||
Disconnected,
|
|
||||||
Connecting,
|
|
||||||
Connected,
|
|
||||||
Listening,
|
|
||||||
PendingRetry
|
|
||||||
};
|
|
||||||
|
|
||||||
enum class CoreMode { None, Client, Server };
|
|
||||||
enum class LogLevel { Error, Info };
|
enum class LogLevel { Error, Info };
|
||||||
enum class RuningState { Started, Stopped };
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
explicit MainWindow(
|
explicit MainWindow(
|
||||||
|
|
@ -89,17 +74,12 @@ public:
|
||||||
~MainWindow() override;
|
~MainWindow() override;
|
||||||
|
|
||||||
void setVisible(bool visible) override;
|
void setVisible(bool visible) override;
|
||||||
CoreMode coreMode() const;
|
CoreMode coreMode() const { return m_CoreProcess.mode(); }
|
||||||
QString coreModeString() const;
|
|
||||||
QString address() const;
|
QString address() const;
|
||||||
QString appPath(const QString &name) const;
|
|
||||||
void open();
|
void open();
|
||||||
ServerConfig &serverConfig() { return m_ServerConfig; }
|
ServerConfig &serverConfig() { return m_ServerConfig; }
|
||||||
void autoAddScreen(const QString name);
|
void autoAddScreen(const QString name);
|
||||||
int showActivationDialog();
|
int showActivationDialog();
|
||||||
void appendLogInfo(const QString &text);
|
|
||||||
void appendLogDebug(const QString &text);
|
|
||||||
void appendLogError(const QString &text);
|
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void created();
|
void created();
|
||||||
|
|
@ -115,19 +95,19 @@ private slots:
|
||||||
void onAppConfigTlsChanged();
|
void onAppConfigTlsChanged();
|
||||||
void onAppConfigScreenNameChanged();
|
void onAppConfigScreenNameChanged();
|
||||||
void onAppConfigInvertConnection();
|
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 onLicenseHandlerSerialKeyChanged(const QString &serialKey);
|
||||||
void onLicenseHandlerInvalidLicense();
|
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 onVersionCheckerUpdateFound(const QString &version);
|
||||||
void onTrayIconCreate(QSystemTrayIcon::ActivationReason reason);
|
void onTrayIconActivated(QSystemTrayIcon::ActivationReason reason);
|
||||||
void onActionStartCoreTriggered();
|
void onActionStartCoreTriggered();
|
||||||
void onActionStopCoreTriggered();
|
void onActionStopCoreTriggered();
|
||||||
void onCoreProcessReadyReadStandardOutput();
|
|
||||||
void onCoreProcessReadyReadStandardError();
|
|
||||||
void onWindowSaveTimerTimeout();
|
void onWindowSaveTimerTimeout();
|
||||||
|
void onServerConnectionConfigureClient(const QString &clientName);
|
||||||
|
|
||||||
// autoconnect slots
|
// autoconnect slots
|
||||||
void on_m_pButtonApply_clicked();
|
void on_m_pButtonApply_clicked();
|
||||||
|
|
@ -145,6 +125,8 @@ private slots:
|
||||||
void on_m_pActivate_triggered();
|
void on_m_pActivate_triggered();
|
||||||
void on_m_pLineEditHostname_returnPressed();
|
void on_m_pLineEditHostname_returnPressed();
|
||||||
void on_m_pLineEditClientIp_returnPressed();
|
void on_m_pLineEditClientIp_returnPressed();
|
||||||
|
void on_m_pLineEditHostname_textChanged(const QString &text);
|
||||||
|
void on_m_pLineEditClientIp_textChanged(const QString &text);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
AppConfig &appConfig() { return m_AppConfig; }
|
AppConfig &appConfig() { return m_AppConfig; }
|
||||||
|
|
@ -154,44 +136,32 @@ private:
|
||||||
void createTrayIcon();
|
void createTrayIcon();
|
||||||
void applyConfig();
|
void applyConfig();
|
||||||
void applyCloseToTray() const;
|
void applyCloseToTray() const;
|
||||||
void setIcon(CoreState state) const;
|
void setIcon(CoreProcess::ConnectionState state);
|
||||||
void setCoreState(CoreState state);
|
|
||||||
bool checkForApp(int which, QString &app);
|
bool checkForApp(int which, QString &app);
|
||||||
bool clientArgs(QStringList &args, QString &app);
|
|
||||||
bool serverArgs(QStringList &args, QString &app);
|
|
||||||
void setStatus(const QString &status);
|
void setStatus(const QString &status);
|
||||||
void sendIpcMessage(IpcMessageType type, const char *buffer, bool showErrors);
|
void sendIpcMessage(IpcMessageType type, const char *buffer, bool showErrors);
|
||||||
void updateFromLogLine(const QString &line);
|
void updateFromLogLine(const QString &line);
|
||||||
QString getIPAddresses() const;
|
QString getIPAddresses() const;
|
||||||
void stopService();
|
|
||||||
void stopDesktop();
|
|
||||||
void enableServer(bool enable);
|
void enableServer(bool enable);
|
||||||
void enableClient(bool enable);
|
void enableClient(bool enable);
|
||||||
QString getProfileRootForArg() const;
|
|
||||||
void checkConnected(const QString &line);
|
void checkConnected(const QString &line);
|
||||||
void checkFingerprint(const QString &line);
|
void checkFingerprint(const QString &line);
|
||||||
bool checkSecureSocket(const QString &line);
|
|
||||||
void checkLicense(const QString &line);
|
void checkLicense(const QString &line);
|
||||||
QString getTimeStamp() const;
|
QString getTimeStamp() const;
|
||||||
void restartCore();
|
|
||||||
void showEvent(QShowEvent *) override;
|
void showEvent(QShowEvent *) override;
|
||||||
void closeEvent(QCloseEvent *event) override;
|
void closeEvent(QCloseEvent *event) override;
|
||||||
void secureSocket(bool secureSocket);
|
void secureSocket(bool secureSocket);
|
||||||
void windowStateChanged();
|
void windowStateChanged();
|
||||||
void connectSlots() const;
|
void connectSlots();
|
||||||
void updateWindowTitle();
|
void updateWindowTitle();
|
||||||
void processCoreLogLine(const QString &line);
|
void handleLogLine(const QString &line);
|
||||||
void startCore();
|
|
||||||
void onCoreProcessRetryStart();
|
|
||||||
void updateLocalFingerprint();
|
void updateLocalFingerprint();
|
||||||
void updateScreenName();
|
void updateScreenName();
|
||||||
void saveSettings();
|
void saveSettings();
|
||||||
QString configFilename();
|
QString configFilename();
|
||||||
bool isCoreActive() const;
|
|
||||||
void showConfigureServer(const QString &message);
|
void showConfigureServer(const QString &message);
|
||||||
void showConfigureServer() { showConfigureServer(""); }
|
void showConfigureServer() { showConfigureServer(""); }
|
||||||
void showLicenseNotice();
|
void showLicenseNotice();
|
||||||
void stopCore();
|
|
||||||
void restoreWindow();
|
void restoreWindow();
|
||||||
void saveWindow();
|
void saveWindow();
|
||||||
void setupControls();
|
void setupControls();
|
||||||
|
|
@ -200,37 +170,28 @@ private:
|
||||||
void showFirstRunMessage();
|
void showFirstRunMessage();
|
||||||
void showDevThanksMessage();
|
void showDevThanksMessage();
|
||||||
QString productName() const;
|
QString productName() const;
|
||||||
|
void updateStatus();
|
||||||
#ifdef Q_OS_MAC
|
|
||||||
void checkOSXNotification(const QString &line);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
VersionChecker m_VersionChecker;
|
VersionChecker m_VersionChecker;
|
||||||
QIpcClient m_IpcClient;
|
synergy::gui::TrayIcon m_TrayIcon;
|
||||||
TrayIcon m_TrayIcon;
|
|
||||||
QMutex m_StopDesktopMutex;
|
|
||||||
bool m_ActivationDialogRunning = false;
|
bool m_ActivationDialogRunning = false;
|
||||||
QStringList m_PendingClientNames;
|
QStringList m_PendingClientNames;
|
||||||
RuningState m_ExpectedRunningState = RuningState::Stopped;
|
|
||||||
std::unique_ptr<QProcess> m_pCoreProcess;
|
|
||||||
QMenuBar *m_pMenuBar = nullptr;
|
QMenuBar *m_pMenuBar = nullptr;
|
||||||
QMenu *m_pMenuFile = nullptr;
|
QMenu *m_pMenuFile = nullptr;
|
||||||
QMenu *m_pMenuEdit = nullptr;
|
QMenu *m_pMenuEdit = nullptr;
|
||||||
QMenu *m_pMenuWindow = nullptr;
|
QMenu *m_pMenuWindow = nullptr;
|
||||||
QMenu *m_pMenuHelp = nullptr;
|
QMenu *m_pMenuHelp = nullptr;
|
||||||
QAbstractButton *m_pCancelButton = nullptr;
|
QAbstractButton *m_pCancelButton = nullptr;
|
||||||
CoreState m_CoreState = CoreState::Disconnected;
|
|
||||||
bool m_AlreadyHidden = false;
|
|
||||||
bool m_SecureSocket = false;
|
bool m_SecureSocket = false;
|
||||||
QString m_SecureSocketVersion = "";
|
|
||||||
bool m_SaveWindow = false;
|
bool m_SaveWindow = false;
|
||||||
LicenseHandler m_LicenseHandler;
|
LicenseHandler m_LicenseHandler;
|
||||||
|
|
||||||
synergy::gui::ConfigScopes &m_ConfigScopes;
|
synergy::gui::ConfigScopes &m_ConfigScopes;
|
||||||
AppConfig &m_AppConfig;
|
AppConfig &m_AppConfig;
|
||||||
ServerConfig m_ServerConfig;
|
ServerConfig m_ServerConfig;
|
||||||
ServerConnection m_ServerConnection;
|
synergy::gui::CoreProcess m_CoreProcess;
|
||||||
ClientConnection m_ClientConnection;
|
synergy::gui::ServerConnection m_ServerConnection;
|
||||||
|
synergy::gui::ClientConnection m_ClientConnection;
|
||||||
synergy::gui::TlsUtility m_TlsUtility;
|
synergy::gui::TlsUtility m_TlsUtility;
|
||||||
QTimer m_WindowSaveTimer;
|
QTimer m_WindowSaveTimer;
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -25,16 +25,13 @@
|
||||||
<property name="windowTitle">
|
<property name="windowTitle">
|
||||||
<string>Synergy</string>
|
<string>Synergy</string>
|
||||||
</property>
|
</property>
|
||||||
<widget class="QWidget" name="centralwidget">
|
<widget class="QWidget" name="m_pWidgetTopLevel">
|
||||||
<layout class="QVBoxLayout" name="verticalLayout_2">
|
<layout class="QVBoxLayout" name="verticalLayout_7">
|
||||||
<property name="spacing">
|
<property name="spacing">
|
||||||
<number>0</number>
|
<number>10</number>
|
||||||
</property>
|
|
||||||
<property name="topMargin">
|
|
||||||
<number>20</number>
|
|
||||||
</property>
|
</property>
|
||||||
<item>
|
<item>
|
||||||
<layout class="QHBoxLayout" name="m_layout1">
|
<layout class="QHBoxLayout" name="m_layoutName">
|
||||||
<item>
|
<item>
|
||||||
<widget class="QLabel" name="m_pLabelComputerName">
|
<widget class="QLabel" name="m_pLabelComputerName">
|
||||||
<property name="text">
|
<property name="text">
|
||||||
|
|
@ -67,22 +64,6 @@
|
||||||
</item>
|
</item>
|
||||||
</layout>
|
</layout>
|
||||||
</item>
|
</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>
|
<item>
|
||||||
<widget class="QLabel" name="m_pLabelIpAddresses">
|
<widget class="QLabel" name="m_pLabelIpAddresses">
|
||||||
<property name="toolTip">
|
<property name="toolTip">
|
||||||
|
|
@ -94,7 +75,7 @@
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item>
|
<item>
|
||||||
<widget class="QWidget" name="m_pMainWidgets" native="true">
|
<widget class="QWidget" name="m_pWidgetModes" native="true">
|
||||||
<property name="sizePolicy">
|
<property name="sizePolicy">
|
||||||
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
|
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
|
||||||
<horstretch>0</horstretch>
|
<horstretch>0</horstretch>
|
||||||
|
|
@ -102,6 +83,21 @@
|
||||||
</sizepolicy>
|
</sizepolicy>
|
||||||
</property>
|
</property>
|
||||||
<layout class="QHBoxLayout" name="horizontalLayout_8">
|
<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>
|
<item>
|
||||||
<widget class="QGroupBox" name="m_pGroupServer">
|
<widget class="QGroupBox" name="m_pGroupServer">
|
||||||
<property name="sizePolicy">
|
<property name="sizePolicy">
|
||||||
|
|
@ -116,6 +112,9 @@
|
||||||
</property>
|
</property>
|
||||||
<item>
|
<item>
|
||||||
<layout class="QVBoxLayout" name="verticalLayout_3">
|
<layout class="QVBoxLayout" name="verticalLayout_3">
|
||||||
|
<property name="spacing">
|
||||||
|
<number>0</number>
|
||||||
|
</property>
|
||||||
<item>
|
<item>
|
||||||
<widget class="QRadioButton" name="m_pRadioGroupServer">
|
<widget class="QRadioButton" name="m_pRadioGroupServer">
|
||||||
<property name="text">
|
<property name="text">
|
||||||
|
|
@ -281,6 +280,9 @@
|
||||||
</property>
|
</property>
|
||||||
<item>
|
<item>
|
||||||
<layout class="QVBoxLayout" name="verticalLayout_6">
|
<layout class="QVBoxLayout" name="verticalLayout_6">
|
||||||
|
<property name="spacing">
|
||||||
|
<number>0</number>
|
||||||
|
</property>
|
||||||
<item>
|
<item>
|
||||||
<widget class="QRadioButton" name="m_pRadioGroupClient">
|
<widget class="QRadioButton" name="m_pRadioGroupClient">
|
||||||
<property name="text">
|
<property name="text">
|
||||||
|
|
@ -388,41 +390,15 @@
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item>
|
<item>
|
||||||
<widget class="QLabel" name="m_pLabelLogs">
|
<widget class="QGroupBox" name="m_pGroupLog">
|
||||||
<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">
|
|
||||||
<property name="minimumSize">
|
<property name="minimumSize">
|
||||||
<size>
|
<size>
|
||||||
<width>0</width>
|
<width>0</width>
|
||||||
<height>0</height>
|
<height>0</height>
|
||||||
</size>
|
</size>
|
||||||
</property>
|
</property>
|
||||||
<property name="frameShape">
|
<property name="title">
|
||||||
<enum>QFrame::StyledPanel</enum>
|
<string>Logs</string>
|
||||||
</property>
|
|
||||||
<property name="frameShadow">
|
|
||||||
<enum>QFrame::Raised</enum>
|
|
||||||
</property>
|
</property>
|
||||||
<layout class="QVBoxLayout" name="verticalLayout_4">
|
<layout class="QVBoxLayout" name="verticalLayout_4">
|
||||||
<property name="leftMargin">
|
<property name="leftMargin">
|
||||||
|
|
@ -469,16 +445,13 @@
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item>
|
<item>
|
||||||
<layout class="QHBoxLayout" name="horizontalLayout">
|
<layout class="QHBoxLayout" name="m_pLayoutActions">
|
||||||
<property name="spacing">
|
<property name="spacing">
|
||||||
<number>6</number>
|
<number>6</number>
|
||||||
</property>
|
</property>
|
||||||
<property name="sizeConstraint">
|
<property name="sizeConstraint">
|
||||||
<enum>QLayout::SetDefaultConstraint</enum>
|
<enum>QLayout::SetDefaultConstraint</enum>
|
||||||
</property>
|
</property>
|
||||||
<property name="topMargin">
|
|
||||||
<number>16</number>
|
|
||||||
</property>
|
|
||||||
<item>
|
<item>
|
||||||
<widget class="QLabel" name="m_pLabelPadlock">
|
<widget class="QLabel" name="m_pLabelPadlock">
|
||||||
<property name="enabled">
|
<property name="enabled">
|
||||||
|
|
@ -599,9 +572,9 @@
|
||||||
</property>
|
</property>
|
||||||
</action>
|
</action>
|
||||||
<action name="m_pActionStartCore">
|
<action name="m_pActionStartCore">
|
||||||
<property name="enabled">
|
<property name="enabled">
|
||||||
<bool>false</bool>
|
<bool>false</bool>
|
||||||
</property>
|
</property>
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>&Start</string>
|
<string>&Start</string>
|
||||||
</property>
|
</property>
|
||||||
|
|
|
||||||
|
|
@ -24,4 +24,8 @@
|
||||||
#include <QtGui>
|
#include <QtGui>
|
||||||
|
|
||||||
QSynergyApplication::QSynergyApplication(int &argc, char **argv)
|
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 <QtCore>
|
||||||
#include <QtGui>
|
#include <QtGui>
|
||||||
|
|
||||||
|
using namespace synergy::gui;
|
||||||
|
|
||||||
ScreenSettingsDialog::ScreenSettingsDialog(
|
ScreenSettingsDialog::ScreenSettingsDialog(
|
||||||
QWidget *parent, Screen *pScreen, const ScreenList *pScreens)
|
QWidget *parent, Screen *pScreen, const ScreenList *pScreens)
|
||||||
: QDialog(parent, Qt::WindowTitleHint | Qt::WindowSystemMenuHint),
|
: QDialog(parent, Qt::WindowTitleHint | Qt::WindowSystemMenuHint),
|
||||||
|
|
|
||||||
|
|
@ -340,8 +340,7 @@ int ServerConfig::autoAddScreen(const QString name) {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (findScreenName(name, targetIndex)) {
|
if (findScreenName(name, targetIndex)) {
|
||||||
m_pMainWindow->appendLogDebug(
|
qDebug("ignoring screen already in config: %s", qPrintable(name));
|
||||||
QString("ignoring screen already in config: %1").arg(name));
|
|
||||||
return kAutoAddScreenIgnore;
|
return kAutoAddScreenIgnore;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -428,7 +427,7 @@ bool ServerConfig::isFull() const {
|
||||||
return isFull;
|
return isFull;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ServerConfig::isScreenExists(const QString &screenName) const {
|
bool ServerConfig::screenExists(const QString &screenName) const {
|
||||||
bool isExists = false;
|
bool isExists = false;
|
||||||
|
|
||||||
for (const auto &screen : screens()) {
|
for (const auto &screen : screens()) {
|
||||||
|
|
|
||||||
|
|
@ -21,6 +21,7 @@
|
||||||
#include "Hotkey.h"
|
#include "Hotkey.h"
|
||||||
#include "ScreenConfig.h"
|
#include "ScreenConfig.h"
|
||||||
#include "ScreenList.h"
|
#include "ScreenList.h"
|
||||||
|
#include "gui/config/IServerConfig.h"
|
||||||
|
|
||||||
#include <QList>
|
#include <QList>
|
||||||
|
|
||||||
|
|
@ -35,9 +36,8 @@ class ServerConfigDialog;
|
||||||
class MainWindow;
|
class MainWindow;
|
||||||
class AppConfig;
|
class AppConfig;
|
||||||
|
|
||||||
class ServerConfig : public ScreenConfig {
|
class ServerConfig : public ScreenConfig, public synergy::gui::IServerConfig {
|
||||||
friend class ServerConfigDialog;
|
friend class ServerConfigDialog;
|
||||||
friend class ServerConnection;
|
|
||||||
friend QTextStream &
|
friend QTextStream &
|
||||||
operator<<(QTextStream &outStream, const ServerConfig &config);
|
operator<<(QTextStream &outStream, const ServerConfig &config);
|
||||||
|
|
||||||
|
|
@ -45,7 +45,7 @@ public:
|
||||||
ServerConfig(
|
ServerConfig(
|
||||||
AppConfig &appConfig, MainWindow &mainWindow,
|
AppConfig &appConfig, MainWindow &mainWindow,
|
||||||
int columns = kDefaultColumns, int rows = kDefaultRows);
|
int columns = kDefaultColumns, int rows = kDefaultRows);
|
||||||
~ServerConfig() = default;
|
~ServerConfig() override = default;
|
||||||
|
|
||||||
bool operator==(const ServerConfig &sc) const;
|
bool operator==(const ServerConfig &sc) const;
|
||||||
|
|
||||||
|
|
@ -64,23 +64,23 @@ public:
|
||||||
int switchCornerSize() const { return m_SwitchCornerSize; }
|
int switchCornerSize() const { return m_SwitchCornerSize; }
|
||||||
const QList<bool> &switchCorners() const { return m_SwitchCorners; }
|
const QList<bool> &switchCorners() const { return m_SwitchCorners; }
|
||||||
const HotkeyList &hotkeys() const { return m_Hotkeys; }
|
const HotkeyList &hotkeys() const { return m_Hotkeys; }
|
||||||
bool enableDragAndDrop() const { return m_EnableDragAndDrop; }
|
bool enableDragAndDrop() const override { return m_EnableDragAndDrop; }
|
||||||
bool disableLockToScreen() const { return m_DisableLockToScreen; }
|
bool disableLockToScreen() const { return m_DisableLockToScreen; }
|
||||||
bool clipboardSharing() const { return m_ClipboardSharing; }
|
bool clipboardSharing() const { return m_ClipboardSharing; }
|
||||||
size_t clipboardSharingSize() const { return m_ClipboardSharingSize; }
|
size_t clipboardSharingSize() const { return m_ClipboardSharingSize; }
|
||||||
static size_t defaultClipboardSharingSize();
|
static size_t defaultClipboardSharingSize();
|
||||||
|
|
||||||
void commit();
|
void commit();
|
||||||
bool save(const QString &fileName) const;
|
bool save(const QString &fileName) const override;
|
||||||
void save(QFile &file) const;
|
void save(QFile &file) const override;
|
||||||
int numScreens() const;
|
int numScreens() const;
|
||||||
int autoAddScreen(const QString name);
|
int autoAddScreen(const QString name);
|
||||||
const QString &getServerName() const;
|
const QString &getServerName() const;
|
||||||
void updateServerName();
|
void updateServerName();
|
||||||
const QString &configFile() const;
|
const QString &configFile() const;
|
||||||
bool useExternalConfig() const;
|
bool useExternalConfig() const;
|
||||||
bool isFull() const;
|
bool isFull() const override;
|
||||||
bool isScreenExists(const QString &screenName) const;
|
bool screenExists(const QString &screenName) const override;
|
||||||
void addClient(const QString &clientName);
|
void addClient(const QString &clientName);
|
||||||
QString getClientAddress() const;
|
QString getClientAddress() const;
|
||||||
void setClientAddress(const QString &address);
|
void setClientAddress(const QString &address);
|
||||||
|
|
|
||||||
|
|
@ -22,7 +22,7 @@
|
||||||
|
|
||||||
#include "ScreenSetupModel.h"
|
#include "ScreenSetupModel.h"
|
||||||
#include "ServerConfig.h"
|
#include "ServerConfig.h"
|
||||||
#include "gui/AppConfig.h"
|
#include "gui/config/AppConfig.h"
|
||||||
#include "ui_ServerConfigDialogBase.h"
|
#include "ui_ServerConfigDialogBase.h"
|
||||||
|
|
||||||
#include <QDialog>
|
#include <QDialog>
|
||||||
|
|
@ -36,9 +36,9 @@ public:
|
||||||
bool addClient(const QString &clientName);
|
bool addClient(const QString &clientName);
|
||||||
|
|
||||||
public slots:
|
public slots:
|
||||||
void accept();
|
void accept() override;
|
||||||
void reject() override;
|
void reject() override;
|
||||||
void showEvent(QShowEvent *event);
|
void showEvent(QShowEvent *event) override;
|
||||||
void message(const QString &message) { m_Message = message; }
|
void message(const QString &message) { m_Message = message; }
|
||||||
|
|
||||||
protected slots:
|
protected slots:
|
||||||
|
|
|
||||||
|
|
@ -76,7 +76,7 @@
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item>
|
<item>
|
||||||
<spacer name="horizontalSpacer_2">
|
<spacer name="horizontalSpacer_21">
|
||||||
<property name="orientation">
|
<property name="orientation">
|
||||||
<enum>Qt::Horizontal</enum>
|
<enum>Qt::Horizontal</enum>
|
||||||
</property>
|
</property>
|
||||||
|
|
@ -117,7 +117,7 @@
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item>
|
<item>
|
||||||
<spacer name="horizontalSpacer_3">
|
<spacer name="horizontalSpacer_22">
|
||||||
<property name="orientation">
|
<property name="orientation">
|
||||||
<enum>Qt::Horizontal</enum>
|
<enum>Qt::Horizontal</enum>
|
||||||
</property>
|
</property>
|
||||||
|
|
@ -342,7 +342,7 @@
|
||||||
<number>9</number>
|
<number>9</number>
|
||||||
</property>
|
</property>
|
||||||
<item row="1" column="0">
|
<item row="1" column="0">
|
||||||
<layout class="QVBoxLayout" name="verticalLayout_2">
|
<layout class="QVBoxLayout" name="verticalLayout_21">
|
||||||
<property name="spacing">
|
<property name="spacing">
|
||||||
<number>0</number>
|
<number>0</number>
|
||||||
</property>
|
</property>
|
||||||
|
|
@ -482,7 +482,7 @@
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item>
|
<item>
|
||||||
<spacer name="verticalSpacer_4">
|
<spacer name="verticalSpacer_41">
|
||||||
<property name="orientation">
|
<property name="orientation">
|
||||||
<enum>Qt::Vertical</enum>
|
<enum>Qt::Vertical</enum>
|
||||||
</property>
|
</property>
|
||||||
|
|
@ -644,7 +644,7 @@
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="1" column="5">
|
<item row="1" column="5">
|
||||||
<layout class="QHBoxLayout" name="horizontalLayout_2">
|
<layout class="QHBoxLayout" name="horizontalLayout_21">
|
||||||
<item>
|
<item>
|
||||||
<widget class="QLabel" name="label">
|
<widget class="QLabel" name="label">
|
||||||
<property name="text">
|
<property name="text">
|
||||||
|
|
@ -1105,7 +1105,7 @@ Enabling this setting will disable the server config GUI.</string>
|
||||||
</layout>
|
</layout>
|
||||||
</item>
|
</item>
|
||||||
<item>
|
<item>
|
||||||
<spacer name="verticalSpacer">
|
<spacer name="verticalSpacer_1">
|
||||||
<property name="orientation">
|
<property name="orientation">
|
||||||
<enum>Qt::Vertical</enum>
|
<enum>Qt::Vertical</enum>
|
||||||
</property>
|
</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 "MainWindow.h"
|
||||||
#include "UpgradeDialog.h"
|
#include "UpgradeDialog.h"
|
||||||
#include "gui/AppConfig.h"
|
#include "gui/config/AppConfig.h"
|
||||||
#include "gui/TlsCertificate.h"
|
|
||||||
#include "gui/constants.h"
|
#include "gui/constants.h"
|
||||||
|
#include "gui/tls/TlsCertificate.h"
|
||||||
|
#include "gui/tls/TlsUtility.h"
|
||||||
#include "validators/ScreenNameValidator.h"
|
#include "validators/ScreenNameValidator.h"
|
||||||
#include "validators/ValidationError.h"
|
#include "validators/ValidationError.h"
|
||||||
|
|
||||||
|
|
@ -31,8 +32,10 @@
|
||||||
#include <QMessageBox>
|
#include <QMessageBox>
|
||||||
#include <QtCore>
|
#include <QtCore>
|
||||||
#include <QtGui>
|
#include <QtGui>
|
||||||
|
#include <qglobal.h>
|
||||||
|
|
||||||
using namespace synergy::license;
|
using namespace synergy::license;
|
||||||
|
using namespace synergy::gui;
|
||||||
|
|
||||||
const char *const kProProductName = "Synergy 1 Pro";
|
const char *const kProProductName = "Synergy 1 Pro";
|
||||||
|
|
||||||
|
|
@ -82,7 +85,7 @@ void SettingsDialog::accept() {
|
||||||
appConfig().setAutoHide(m_pCheckBoxAutoHide->isChecked());
|
appConfig().setAutoHide(m_pCheckBoxAutoHide->isChecked());
|
||||||
appConfig().setPreventSleep(m_pCheckBoxPreventSleep->isChecked());
|
appConfig().setPreventSleep(m_pCheckBoxPreventSleep->isChecked());
|
||||||
appConfig().setTlsCertPath(m_pLineEditCertificatePath->text());
|
appConfig().setTlsCertPath(m_pLineEditCertificatePath->text());
|
||||||
appConfig().setTlsKeyLength(m_pComboBoxKeyLength->currentText());
|
appConfig().setTlsKeyLength(m_pComboBoxKeyLength->currentText().toInt());
|
||||||
appConfig().setTlsEnabled(m_pCheckBoxEnableCrypto->isChecked());
|
appConfig().setTlsEnabled(m_pCheckBoxEnableCrypto->isChecked());
|
||||||
appConfig().setLanguageSync(m_pCheckBoxLanguageSync->isChecked());
|
appConfig().setLanguageSync(m_pCheckBoxLanguageSync->isChecked());
|
||||||
appConfig().setInvertScrollDirection(m_pCheckBoxScrollDirection->isChecked());
|
appConfig().setInvertScrollDirection(m_pCheckBoxScrollDirection->isChecked());
|
||||||
|
|
@ -136,8 +139,9 @@ void SettingsDialog::updateTlsControls() {
|
||||||
if (QFile(appConfig().tlsCertPath()).exists()) {
|
if (QFile(appConfig().tlsCertPath()).exists()) {
|
||||||
updateKeyLengthOnFile(appConfig().tlsCertPath());
|
updateKeyLengthOnFile(appConfig().tlsCertPath());
|
||||||
} else {
|
} else {
|
||||||
|
const auto keyLengthText = QString::number(appConfig().tlsKeyLength());
|
||||||
m_pComboBoxKeyLength->setCurrentIndex(
|
m_pComboBoxKeyLength->setCurrentIndex(
|
||||||
m_pComboBoxKeyLength->findText(appConfig().tlsKeyLength()));
|
m_pComboBoxKeyLength->findText(keyLengthText));
|
||||||
}
|
}
|
||||||
|
|
||||||
m_pCheckBoxEnableCrypto->setChecked(m_appConfig.tlsEnabled());
|
m_pCheckBoxEnableCrypto->setChecked(m_appConfig.tlsEnabled());
|
||||||
|
|
@ -189,7 +193,7 @@ void SettingsDialog::on_m_pButtonBrowseLog_clicked() {
|
||||||
void SettingsDialog::on_m_pCheckBoxEnableCrypto_clicked(bool) {
|
void SettingsDialog::on_m_pCheckBoxEnableCrypto_clicked(bool) {
|
||||||
updateTlsControlsEnabled();
|
updateTlsControlsEnabled();
|
||||||
|
|
||||||
if (kLicensingEnabled && !m_tlsUtility.isAvailable()) {
|
if (kEnableActivation && !m_tlsUtility.isAvailable()) {
|
||||||
auto edition = m_license.productEdition();
|
auto edition = m_license.productEdition();
|
||||||
if (edition == Edition::kBasic) {
|
if (edition == Edition::kBasic) {
|
||||||
UpgradeDialog upgradeDialog(this);
|
UpgradeDialog upgradeDialog(this);
|
||||||
|
|
@ -215,9 +219,11 @@ void SettingsDialog::on_m_pPushButtonBrowseCert_clicked() {
|
||||||
|
|
||||||
if (!fileName.isEmpty()) {
|
if (!fileName.isEmpty()) {
|
||||||
m_pLineEditCertificatePath->setText(fileName);
|
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);
|
updateKeyLengthOnFile(fileName);
|
||||||
|
} else {
|
||||||
|
qDebug("no tls certificate file at: %s", qUtf8Printable(fileName));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
updateTlsRegenerateButton();
|
updateTlsRegenerateButton();
|
||||||
|
|
@ -228,9 +234,8 @@ void SettingsDialog::on_m_pComboBoxKeyLength_currentIndexChanged(int index) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void SettingsDialog::updateTlsRegenerateButton() {
|
void SettingsDialog::updateTlsRegenerateButton() {
|
||||||
// Disable the Regenerate cert button if the key length is different to saved
|
const auto keyLength = m_pComboBoxKeyLength->currentText().toInt();
|
||||||
auto keyChanged =
|
auto keyChanged = appConfig().tlsKeyLength() != keyLength;
|
||||||
appConfig().tlsKeyLength() != m_pComboBoxKeyLength->currentText();
|
|
||||||
auto pathChanged =
|
auto pathChanged =
|
||||||
appConfig().tlsCertPath() != m_pLineEditCertificatePath->text();
|
appConfig().tlsCertPath() != m_pLineEditCertificatePath->text();
|
||||||
// NOR the above bools, if any have changed regen should be disabled as it
|
// 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() {
|
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) {
|
void SettingsDialog::updateKeyLengthOnFile(const QString &path) {
|
||||||
TlsCertificate ssl;
|
TlsCertificate ssl;
|
||||||
|
if (!QFile(path).exists()) {
|
||||||
|
qFatal("tls certificate file not found: %s", qUtf8Printable(path));
|
||||||
|
}
|
||||||
|
|
||||||
auto length = ssl.getCertKeyLength(path);
|
auto length = ssl.getCertKeyLength(path);
|
||||||
auto index = m_pComboBoxKeyLength->findText(length);
|
auto index = m_pComboBoxKeyLength->findText(QString::number(length));
|
||||||
m_pComboBoxKeyLength->setCurrentIndex(index);
|
m_pComboBoxKeyLength->setCurrentIndex(index);
|
||||||
// Also update what is in the appconfig to match the file itself
|
|
||||||
appConfig().setTlsKeyLength(length);
|
appConfig().setTlsKeyLength(length);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -20,8 +20,8 @@
|
||||||
|
|
||||||
#include "ui_SettingsDialogBase.h"
|
#include "ui_SettingsDialogBase.h"
|
||||||
|
|
||||||
#include "gui/CoreInterface.h"
|
#include "gui/core/CoreTool.h"
|
||||||
#include "gui/TlsUtility.h"
|
#include "gui/tls/TlsUtility.h"
|
||||||
#include "license/License.h"
|
#include "license/License.h"
|
||||||
#include "validators/ValidationError.h"
|
#include "validators/ValidationError.h"
|
||||||
|
|
||||||
|
|
@ -68,7 +68,7 @@ protected:
|
||||||
private:
|
private:
|
||||||
MainWindow *m_pMainWindow;
|
MainWindow *m_pMainWindow;
|
||||||
AppConfig &m_appConfig;
|
AppConfig &m_appConfig;
|
||||||
CoreInterface m_coreInterface;
|
[[no_unique_address]] CoreTool m_coreTool;
|
||||||
const synergy::license::License &m_license;
|
const synergy::license::License &m_license;
|
||||||
synergy::gui::TlsUtility m_tlsUtility;
|
synergy::gui::TlsUtility m_tlsUtility;
|
||||||
validators::ValidationError *m_pScreenNameError;
|
validators::ValidationError *m_pScreenNameError;
|
||||||
|
|
|
||||||
|
|
@ -21,6 +21,8 @@
|
||||||
#include "validators/ScreenNameValidator.h"
|
#include "validators/ScreenNameValidator.h"
|
||||||
#include "validators/ValidationError.h"
|
#include "validators/ValidationError.h"
|
||||||
|
|
||||||
|
using namespace synergy::gui;
|
||||||
|
|
||||||
SetupWizard::SetupWizard(AppConfig &appConfig) : m_appConfig(appConfig) {
|
SetupWizard::SetupWizard(AppConfig &appConfig) : m_appConfig(appConfig) {
|
||||||
setupUi(this);
|
setupUi(this);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -19,7 +19,7 @@
|
||||||
|
|
||||||
#include "ui_SetupWizardBase.h"
|
#include "ui_SetupWizardBase.h"
|
||||||
|
|
||||||
#include "gui/AppConfig.h"
|
#include "gui/config/AppConfig.h"
|
||||||
|
|
||||||
#include <QDialog>
|
#include <QDialog>
|
||||||
#include <QObject>
|
#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 "QSynergyApplication.h"
|
||||||
#include "SetupWizard.h"
|
#include "SetupWizard.h"
|
||||||
#include "SetupWizardBlocker.h"
|
#include "SetupWizardBlocker.h"
|
||||||
#include "gui/AppConfig.h"
|
#include "gui/Logger.h"
|
||||||
#include "gui/ConfigScopes.h"
|
#include "gui/config/AppConfig.h"
|
||||||
|
#include "gui/config/ConfigScopes.h"
|
||||||
#include "gui/constants.h"
|
#include "gui/constants.h"
|
||||||
#include "gui/dotenv.h"
|
#include "gui/dotenv.h"
|
||||||
#include "gui/messages.h"
|
#include "gui/messages.h"
|
||||||
|
|
@ -54,7 +55,7 @@ bool checkMacAssistiveDevices();
|
||||||
|
|
||||||
int main(int argc, char *argv[]) {
|
int main(int argc, char *argv[]) {
|
||||||
|
|
||||||
#ifdef Q_OS_DARWIN
|
#if defined(Q_OS_MAC)
|
||||||
/* Workaround for QTBUG-40332 - "High ping when QNetworkAccessManager is
|
/* Workaround for QTBUG-40332 - "High ping when QNetworkAccessManager is
|
||||||
* instantiated" */
|
* instantiated" */
|
||||||
::setenv("QT_BEARER_POLL_TIMEOUT", "-1", 1);
|
::setenv("QT_BEARER_POLL_TIMEOUT", "-1", 1);
|
||||||
|
|
@ -67,9 +68,10 @@ int main(int argc, char *argv[]) {
|
||||||
QSynergyApplication app(argc, argv);
|
QSynergyApplication app(argc, argv);
|
||||||
qInstallMessageHandler(synergy::gui::messages::messageHandler);
|
qInstallMessageHandler(synergy::gui::messages::messageHandler);
|
||||||
|
|
||||||
qInfo("Synergy v%s", synergy::gui::version().toUtf8().constData());
|
|
||||||
|
|
||||||
dotenv(".env");
|
dotenv(".env");
|
||||||
|
Logger::instance().loadEnvVars();
|
||||||
|
|
||||||
|
qInfo("Synergy v%s", synergy::gui::version().toUtf8().constData());
|
||||||
|
|
||||||
#if defined(Q_OS_MAC)
|
#if defined(Q_OS_MAC)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -18,7 +18,10 @@
|
||||||
#include "LineEditValidator.h"
|
#include "LineEditValidator.h"
|
||||||
|
|
||||||
#include "gui/styles.h"
|
#include "gui/styles.h"
|
||||||
#include <qvalidator.h>
|
|
||||||
|
#include <QValidator>
|
||||||
|
|
||||||
|
using namespace synergy::gui;
|
||||||
|
|
||||||
namespace validators {
|
namespace validators {
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -19,6 +19,8 @@
|
||||||
|
|
||||||
#include "gui/styles.h"
|
#include "gui/styles.h"
|
||||||
|
|
||||||
|
using namespace synergy::gui;
|
||||||
|
|
||||||
namespace validators {
|
namespace validators {
|
||||||
|
|
||||||
void clear(QLabel *label) {
|
void clear(QLabel *label) {
|
||||||
|
|
|
||||||
|
|
@ -18,8 +18,8 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <QLabel>
|
#include <QLabel>
|
||||||
|
#include <QObject>
|
||||||
#include <QString>
|
#include <QString>
|
||||||
#include <qobject.h>
|
|
||||||
|
|
||||||
namespace validators {
|
namespace validators {
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -18,7 +18,9 @@
|
||||||
|
|
||||||
#include "ServerStateLabel.h"
|
#include "ServerStateLabel.h"
|
||||||
|
|
||||||
#include "ServerMessage.h"
|
#include "gui/core/ServerMessage.h"
|
||||||
|
|
||||||
|
using namespace synergy::gui;
|
||||||
|
|
||||||
namespace synergy_widgets {
|
namespace synergy_widgets {
|
||||||
|
|
||||||
|
|
@ -38,7 +40,14 @@ void ServerStateLabel::updateServerState(const QString &line) {
|
||||||
if (m_clients.isEmpty()) {
|
if (m_clients.isEmpty()) {
|
||||||
setText(tr("No clients connected"));
|
setText(tr("No clients connected"));
|
||||||
} else {
|
} 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 sources *.cpp)
|
||||||
file(GLOB_RECURSE headers *.h)
|
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)
|
if(ADD_HEADERS_TO_SOURCES)
|
||||||
list(APPEND sources ${headers})
|
list(APPEND sources ${headers})
|
||||||
endif()
|
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
|
* 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
|
* This package is free software; you can redistribute it and/or
|
||||||
* modify it under the terms of the GNU General Public License
|
* modify it under the terms of the GNU General Public License
|
||||||
|
|
@ -15,9 +15,7 @@
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#if !defined(OSXHELPERS__H)
|
#pragma once
|
||||||
|
|
||||||
#define OSXHELPERS__H
|
|
||||||
|
|
||||||
#include <QString>
|
#include <QString>
|
||||||
|
|
||||||
|
|
@ -28,5 +26,3 @@ bool isOSXDevelopmentBuild();
|
||||||
bool showOSXNotification(const QString &title, const QString &body);
|
bool showOSXNotification(const QString &title, const QString &body);
|
||||||
bool isOSXInterfaceStyleDark();
|
bool isOSXInterfaceStyleDark();
|
||||||
IconsTheme getOSXIconsTheme();
|
IconsTheme getOSXIconsTheme();
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
/*
|
/*
|
||||||
* synergy -- mouse and keyboard sharing utility
|
* 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
|
* This package is free software; you can redistribute it and/or
|
||||||
* modify it under the terms of the GNU General Public License
|
* modify it under the terms of the GNU General Public License
|
||||||
|
|
@ -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 <QtNetwork>
|
||||||
#include <functional>
|
#include <functional>
|
||||||
|
|
||||||
using ConfigScopes = synergy::gui::ConfigScopes;
|
using namespace synergy::gui;
|
||||||
using IConfigScopes = synergy::gui::IConfigScopes;
|
|
||||||
|
|
||||||
// this should be incremented each time the wizard is changed,
|
// this should be incremented each time the wizard is changed,
|
||||||
// which will force it to re-run for existing installations.
|
// which will force it to re-run for existing installations.
|
||||||
const int kWizardVersion = 8;
|
const int kWizardVersion = 8;
|
||||||
|
|
||||||
|
static const char *const kLogLevelNames[] = {
|
||||||
|
"INFO", "DEBUG", "DEBUG1", "DEBUG2"};
|
||||||
|
|
||||||
#if defined(Q_OS_WIN)
|
#if defined(Q_OS_WIN)
|
||||||
const char AppConfig::m_CoreServerName[] = "synergys.exe";
|
const char AppConfig::m_CoreServerName[] = "synergys.exe";
|
||||||
const char AppConfig::m_CoreClientName[] = "synergyc.exe";
|
const char AppConfig::m_CoreClientName[] = "synergyc.exe";
|
||||||
|
|
@ -94,11 +96,9 @@ const char *const AppConfig::m_SettingsName[] = {
|
||||||
"showCloseReminder",
|
"showCloseReminder",
|
||||||
};
|
};
|
||||||
|
|
||||||
static const char *logLevelNames[] = {"INFO", "DEBUG", "DEBUG1", "DEBUG2"};
|
|
||||||
|
|
||||||
AppConfig::AppConfig(
|
AppConfig::AppConfig(
|
||||||
synergy::gui::IConfigScopes &scopes, std::shared_ptr<Deps> deps)
|
synergy::gui::IConfigScopes &scopes, std::shared_ptr<Deps> deps)
|
||||||
: m_scopes(scopes),
|
: m_Scopes(scopes),
|
||||||
m_pDeps(deps),
|
m_pDeps(deps),
|
||||||
m_ScreenName(deps->hostname()) {
|
m_ScreenName(deps->hostname()) {
|
||||||
determineScope();
|
determineScope();
|
||||||
|
|
@ -120,8 +120,8 @@ void AppConfig::recallFromAllScopes() {
|
||||||
m_WizardLastRun = findInAllScopes(kWizardLastRun, m_WizardLastRun).toInt();
|
m_WizardLastRun = findInAllScopes(kWizardLastRun, m_WizardLastRun).toInt();
|
||||||
m_LoadFromSystemScope =
|
m_LoadFromSystemScope =
|
||||||
findInAllScopes(kLoadSystemSettings, m_LoadFromSystemScope).toBool();
|
findInAllScopes(kLoadSystemSettings, m_LoadFromSystemScope).toBool();
|
||||||
m_licenseNextCheck =
|
m_LicenseNextCheck =
|
||||||
findInAllScopes(kLicenseNextCheck, m_licenseNextCheck).toULongLong();
|
findInAllScopes(kLicenseNextCheck, m_LicenseNextCheck).toULongLong();
|
||||||
}
|
}
|
||||||
|
|
||||||
void AppConfig::recallFromCurrentScope() {
|
void AppConfig::recallFromCurrentScope() {
|
||||||
|
|
@ -165,8 +165,7 @@ void AppConfig::recallFromCurrentScope() {
|
||||||
m_CloseToTray = getFromCurrentScope(kCloseToTray, m_CloseToTray).toBool();
|
m_CloseToTray = getFromCurrentScope(kCloseToTray, m_CloseToTray).toBool();
|
||||||
m_TlsEnabled = getFromCurrentScope(kTlsEnabled, m_TlsEnabled).toBool();
|
m_TlsEnabled = getFromCurrentScope(kTlsEnabled, m_TlsEnabled).toBool();
|
||||||
m_TlsCertPath = getFromCurrentScope(kTlsCertPath, m_TlsCertPath).toString();
|
m_TlsCertPath = getFromCurrentScope(kTlsCertPath, m_TlsCertPath).toString();
|
||||||
m_TlsKeyLength =
|
m_TlsKeyLength = getFromCurrentScope(kTlsKeyLength, m_TlsKeyLength).toInt();
|
||||||
getFromCurrentScope(kTlsKeyLength, m_TlsKeyLength).toString();
|
|
||||||
m_MainWindowPosition = getFromCurrentScope<QPoint>(
|
m_MainWindowPosition = getFromCurrentScope<QPoint>(
|
||||||
kMainWindowPosition, [](const QVariant &v) { return v.toPoint(); });
|
kMainWindowPosition, [](const QVariant &v) { return v.toPoint(); });
|
||||||
m_MainWindowSize = getFromCurrentScope<QSize>(
|
m_MainWindowSize = getFromCurrentScope<QSize>(
|
||||||
|
|
@ -197,13 +196,13 @@ void AppConfig::recallScreenName() {
|
||||||
void AppConfig::commit() {
|
void AppConfig::commit() {
|
||||||
using enum Setting;
|
using enum Setting;
|
||||||
|
|
||||||
qDebug("comitting app config");
|
qDebug("committing app config");
|
||||||
|
|
||||||
saveToAllScopes(kWizardLastRun, m_WizardLastRun);
|
saveToAllScopes(kWizardLastRun, m_WizardLastRun);
|
||||||
saveToAllScopes(kLoadSystemSettings, m_LoadFromSystemScope);
|
saveToAllScopes(kLoadSystemSettings, m_LoadFromSystemScope);
|
||||||
saveToAllScopes(kClientGroupChecked, m_ClientGroupChecked);
|
saveToAllScopes(kClientGroupChecked, m_ClientGroupChecked);
|
||||||
saveToAllScopes(kServerGroupChecked, m_ServerGroupChecked);
|
saveToAllScopes(kServerGroupChecked, m_ServerGroupChecked);
|
||||||
saveToAllScopes(kLicenseNextCheck, m_licenseNextCheck);
|
saveToAllScopes(kLicenseNextCheck, m_LicenseNextCheck);
|
||||||
|
|
||||||
if (isActiveScopeWritable()) {
|
if (isActiveScopeWritable()) {
|
||||||
setInCurrentScope(kScreenName, m_ScreenName);
|
setInCurrentScope(kScreenName, m_ScreenName);
|
||||||
|
|
@ -214,7 +213,8 @@ void AppConfig::commit() {
|
||||||
setInCurrentScope(kLogFilename, m_LogFilename);
|
setInCurrentScope(kLogFilename, m_LogFilename);
|
||||||
setInCurrentScope(kStartedBefore, m_StartedBefore);
|
setInCurrentScope(kStartedBefore, m_StartedBefore);
|
||||||
setInCurrentScope(kElevateMode, static_cast<int>(m_ElevateMode));
|
setInCurrentScope(kElevateMode, static_cast<int>(m_ElevateMode));
|
||||||
setInCurrentScope(kElevateModeLegacy, m_ElevateMode == ElevateAlways);
|
setInCurrentScope(
|
||||||
|
kElevateModeLegacy, m_ElevateMode == ElevateMode::kAlways);
|
||||||
setInCurrentScope(kTlsEnabled, m_TlsEnabled);
|
setInCurrentScope(kTlsEnabled, m_TlsEnabled);
|
||||||
setInCurrentScope(kAutoHide, m_AutoHide);
|
setInCurrentScope(kAutoHide, m_AutoHide);
|
||||||
setInCurrentScope(kSerialKey, m_SerialKey);
|
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
|
// first, try to determine if the system scope should be used according to the
|
||||||
// user scope...
|
// user scope...
|
||||||
if (m_scopes.scopeContains(
|
if (m_Scopes.scopeContains(
|
||||||
settingName(Setting::kLoadSystemSettings),
|
settingName(Setting::kLoadSystemSettings),
|
||||||
ConfigScopes::Scope::User)) {
|
ConfigScopes::Scope::User)) {
|
||||||
auto loadFromSystemScope =
|
auto loadFromSystemScope =
|
||||||
m_scopes
|
m_Scopes
|
||||||
.getFromScope(
|
.getFromScope(
|
||||||
settingName(Setting::kLoadSystemSettings),
|
settingName(Setting::kLoadSystemSettings),
|
||||||
m_LoadFromSystemScope, ConfigScopes::Scope::User)
|
m_LoadFromSystemScope, ConfigScopes::Scope::User)
|
||||||
|
|
@ -265,7 +265,7 @@ void AppConfig::determineScope() {
|
||||||
// ...failing that, check the system scope instead to see if an arbitrary
|
// ...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
|
// required setting is present. if it is, then we can assume that the system
|
||||||
// scope should be used.
|
// scope should be used.
|
||||||
else if (m_scopes.scopeContains(
|
else if (m_Scopes.scopeContains(
|
||||||
settingName(Setting::kScreenName),
|
settingName(Setting::kScreenName),
|
||||||
ConfigScopes::Scope::System)) {
|
ConfigScopes::Scope::System)) {
|
||||||
setLoadFromSystemScope(true);
|
setLoadFromSystemScope(true);
|
||||||
|
|
@ -275,7 +275,7 @@ void AppConfig::determineScope() {
|
||||||
void AppConfig::recallSerialKey() {
|
void AppConfig::recallSerialKey() {
|
||||||
using enum Setting;
|
using enum Setting;
|
||||||
|
|
||||||
if (!m_scopes.scopeContains(settingName(kLoadSystemSettings))) {
|
if (!m_Scopes.scopeContains(settingName(kLoadSystemSettings))) {
|
||||||
qDebug("no serial key in current scope, skipping");
|
qDebug("no serial key in current scope, skipping");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
@ -294,7 +294,7 @@ void AppConfig::recallSerialKey() {
|
||||||
void AppConfig::recallElevateMode() {
|
void AppConfig::recallElevateMode() {
|
||||||
using enum Setting;
|
using enum Setting;
|
||||||
|
|
||||||
if (!m_scopes.scopeContains(settingName(kElevateMode))) {
|
if (!m_Scopes.scopeContains(settingName(kElevateMode))) {
|
||||||
qDebug("elevate mode not set yet, skipping");
|
qDebug("elevate mode not set yet, skipping");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
@ -322,24 +322,24 @@ QString AppConfig::settingName(Setting name) {
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename T> void AppConfig::setInCurrentScope(Setting name, T value) {
|
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) {
|
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::User);
|
||||||
m_scopes.setInScope(settingName(name), value, ConfigScopes::Scope::System);
|
m_Scopes.setInScope(settingName(name), value, ConfigScopes::Scope::System);
|
||||||
}
|
}
|
||||||
|
|
||||||
QVariant AppConfig::getFromCurrentScope(
|
QVariant AppConfig::getFromCurrentScope(
|
||||||
Setting name, const QVariant &defaultValue) const {
|
Setting name, const QVariant &defaultValue) const {
|
||||||
return m_scopes.getFromScope(settingName(name), defaultValue);
|
return m_Scopes.getFromScope(settingName(name), defaultValue);
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
std::optional<T> AppConfig::getFromCurrentScope(
|
std::optional<T> AppConfig::getFromCurrentScope(
|
||||||
Setting name, std::function<T(const QVariant &)> toType) const {
|
Setting name, std::function<T(const QVariant &)> toType) const {
|
||||||
if (m_scopes.scopeContains(settingName(name))) {
|
if (m_Scopes.scopeContains(settingName(name))) {
|
||||||
return toType(m_scopes.getFromScope(settingName(name)));
|
return toType(m_Scopes.getFromScope(settingName(name)));
|
||||||
} else {
|
} else {
|
||||||
return std::nullopt;
|
return std::nullopt;
|
||||||
}
|
}
|
||||||
|
|
@ -348,7 +348,7 @@ std::optional<T> AppConfig::getFromCurrentScope(
|
||||||
template <typename T>
|
template <typename T>
|
||||||
void AppConfig::setInCurrentScope(Setting name, const std::optional<T> &value) {
|
void AppConfig::setInCurrentScope(Setting name, const std::optional<T> &value) {
|
||||||
if (value.has_value()) {
|
if (value.has_value()) {
|
||||||
m_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);
|
QVariant result(defaultValue);
|
||||||
QString setting(settingName(name));
|
QString setting(settingName(name));
|
||||||
|
|
||||||
if (m_scopes.scopeContains(setting)) {
|
if (m_Scopes.scopeContains(setting)) {
|
||||||
result = m_scopes.getFromScope(setting, defaultValue);
|
result = m_Scopes.getFromScope(setting, defaultValue);
|
||||||
} else if (m_scopes.activeScope() == System) {
|
} else if (m_Scopes.activeScope() == System) {
|
||||||
if (m_scopes.scopeContains(setting, User)) {
|
if (m_Scopes.scopeContains(setting, User)) {
|
||||||
result = m_scopes.getFromScope(setting, defaultValue, User);
|
result = m_Scopes.getFromScope(setting, defaultValue, User);
|
||||||
}
|
}
|
||||||
} else if (m_scopes.scopeContains(setting, System)) {
|
} else if (m_Scopes.scopeContains(setting, System)) {
|
||||||
result = m_scopes.getFromScope(setting, defaultValue, System);
|
result = m_Scopes.getFromScope(setting, defaultValue, System);
|
||||||
}
|
}
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
|
|
@ -388,18 +388,18 @@ void AppConfig::loadScope(ConfigScopes::Scope scope) {
|
||||||
qFatal("invalid scope");
|
qFatal("invalid scope");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (m_scopes.activeScope() == scope) {
|
if (m_Scopes.activeScope() == scope) {
|
||||||
qDebug("already in required scope, skipping");
|
qDebug("already in required scope, skipping");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
m_scopes.setActiveScope(scope);
|
m_Scopes.setActiveScope(scope);
|
||||||
|
|
||||||
// only signal ready if there is at least one setting in the required scope.
|
// only signal ready if there is at least one setting in the required scope.
|
||||||
// this prevents the current settings from being set back to default.
|
// this prevents the current settings from being set back to default.
|
||||||
if (m_scopes.scopeContains(
|
if (m_Scopes.scopeContains(
|
||||||
settingName(Setting::kScreenName), m_scopes.activeScope())) {
|
settingName(Setting::kScreenName), m_Scopes.activeScope())) {
|
||||||
m_scopes.signalReady();
|
m_Scopes.signalReady();
|
||||||
} else {
|
} else {
|
||||||
qDebug("no screen name in scope, skipping");
|
qDebug("no screen name in scope, skipping");
|
||||||
}
|
}
|
||||||
|
|
@ -421,11 +421,11 @@ void AppConfig::setLoadFromSystemScope(bool value) {
|
||||||
}
|
}
|
||||||
|
|
||||||
bool AppConfig::isActiveScopeWritable() const {
|
bool AppConfig::isActiveScopeWritable() const {
|
||||||
return m_scopes.isActiveScopeWritable();
|
return m_Scopes.isActiveScopeWritable();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool AppConfig::isActiveScopeSystem() const {
|
bool AppConfig::isActiveScopeSystem() const {
|
||||||
return m_scopes.activeScope() == ConfigScopes::Scope::System;
|
return m_Scopes.activeScope() == ConfigScopes::Scope::System;
|
||||||
}
|
}
|
||||||
|
|
||||||
QString AppConfig::logDir() const {
|
QString AppConfig::logDir() const {
|
||||||
|
|
@ -446,7 +446,7 @@ void AppConfig::persistLogDir() const {
|
||||||
// Begin getters
|
// Begin getters
|
||||||
///////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
IConfigScopes &AppConfig::scopes() { return m_scopes; }
|
IConfigScopes &AppConfig::scopes() { return m_Scopes; }
|
||||||
|
|
||||||
bool AppConfig::activationHasRun() const { return m_ActivationHasRun; }
|
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; }
|
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 {
|
ProcessMode AppConfig::processMode() const {
|
||||||
return m_EnableService ? ProcessMode::kService : ProcessMode::kDesktop;
|
return m_EnableService ? ProcessMode::kService : ProcessMode::kDesktop;
|
||||||
|
|
@ -493,7 +493,7 @@ bool AppConfig::invertScrollDirection() const {
|
||||||
}
|
}
|
||||||
|
|
||||||
unsigned long long AppConfig::licenseNextCheck() const {
|
unsigned long long AppConfig::licenseNextCheck() const {
|
||||||
return m_licenseNextCheck;
|
return m_LicenseNextCheck;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool AppConfig::languageSync() const { return m_LanguageSync; }
|
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::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; }
|
bool AppConfig::enableService() const { return m_EnableService; }
|
||||||
|
|
||||||
|
|
@ -555,18 +555,28 @@ void AppConfig::setTlsEnabled(bool value) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void AppConfig::setTlsCertPath(const QString &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;
|
m_TlsCertPath = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
void AppConfig::setTlsKeyLength(const QString &value) {
|
void AppConfig::setTlsKeyLength(int value) {
|
||||||
m_TlsChanged = m_TlsKeyLength != 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;
|
m_TlsKeyLength = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
void AppConfig::setSerialKey(const QString &serialKey) {
|
void AppConfig::setSerialKey(const QString &serialKey) {
|
||||||
m_SerialKey = serialKey;
|
m_SerialKey = serialKey;
|
||||||
saveToAllScopes(Setting::kSerialKey, m_SerialKey);
|
saveToAllScopes(Setting::kSerialKey, m_SerialKey);
|
||||||
}
|
}
|
||||||
|
|
||||||
void AppConfig::setServerGroupChecked(bool newValue) {
|
void AppConfig::setServerGroupChecked(bool newValue) {
|
||||||
m_ServerGroupChecked = 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::setAutoHide(bool b) { m_AutoHide = b; }
|
||||||
|
|
||||||
void AppConfig::setLicenseNextCheck(unsigned long long time) {
|
void AppConfig::setLicenseNextCheck(unsigned long long time) {
|
||||||
m_licenseNextCheck = time;
|
m_LicenseNextCheck = time;
|
||||||
}
|
}
|
||||||
|
|
||||||
void AppConfig::setInvertScrollDirection(bool newValue) {
|
void AppConfig::setInvertScrollDirection(bool newValue) {
|
||||||
|
|
@ -18,10 +18,10 @@
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "ConfigScopes.h"
|
|
||||||
#include "CoreInterface.h"
|
|
||||||
#include "ElevateMode.h"
|
#include "ElevateMode.h"
|
||||||
#include "IAppConfig.h"
|
#include "IAppConfig.h"
|
||||||
|
#include "IConfigScopes.h"
|
||||||
|
#include "gui/core/CoreTool.h"
|
||||||
|
|
||||||
#include <QDir>
|
#include <QDir>
|
||||||
#include <QHostInfo>
|
#include <QHostInfo>
|
||||||
|
|
@ -32,10 +32,11 @@
|
||||||
#include <QVariant>
|
#include <QVariant>
|
||||||
#include <optional>
|
#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 QString kDefaultLogFile = "synergy.log";
|
||||||
|
const int kDefaultTlsKeyLength = 2048;
|
||||||
|
|
||||||
#if defined(Q_OS_WIN)
|
#if defined(Q_OS_WIN)
|
||||||
const ProcessMode kDefaultProcessMode = ProcessMode::kService;
|
const ProcessMode kDefaultProcessMode = ProcessMode::kService;
|
||||||
|
|
@ -49,6 +50,8 @@ const bool kDefaultShowDevThanks = true;
|
||||||
const bool kDefaultShowDevThanks = false;
|
const bool kDefaultShowDevThanks = false;
|
||||||
#endif // SYNERGY_SHOW_DEV_THANKS
|
#endif // SYNERGY_SHOW_DEV_THANKS
|
||||||
|
|
||||||
|
} // namespace synergy::gui
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Simply reads and writes app settings.
|
* @brief Simply reads and writes app settings.
|
||||||
*
|
*
|
||||||
|
|
@ -58,6 +61,9 @@ const bool kDefaultShowDevThanks = false;
|
||||||
* becoming a god object.
|
* becoming a god object.
|
||||||
*/
|
*/
|
||||||
class AppConfig : public QObject, public synergy::gui::IAppConfig {
|
class AppConfig : public QObject, public synergy::gui::IAppConfig {
|
||||||
|
using ProcessMode = synergy::gui::ProcessMode;
|
||||||
|
using IConfigScopes = synergy::gui::IConfigScopes;
|
||||||
|
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
@ -110,20 +116,19 @@ private:
|
||||||
public:
|
public:
|
||||||
struct Deps {
|
struct Deps {
|
||||||
virtual ~Deps() = default;
|
virtual ~Deps() = default;
|
||||||
virtual QString profileDir() const {
|
virtual QString profileDir() const { return m_coreTool.getProfileDir(); }
|
||||||
return m_coreInterface.getProfileDir();
|
|
||||||
}
|
|
||||||
virtual QString hostname() const { return QHostInfo::localHostName(); }
|
virtual QString hostname() const { return QHostInfo::localHostName(); }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
[[no_unique_address]] CoreInterface m_coreInterface;
|
[[no_unique_address]] CoreTool m_coreTool;
|
||||||
};
|
};
|
||||||
|
|
||||||
explicit AppConfig(
|
explicit AppConfig(
|
||||||
synergy::gui::IConfigScopes &scopes,
|
IConfigScopes &scopes,
|
||||||
std::shared_ptr<Deps> deps = std::make_shared<Deps>());
|
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.
|
* @brief Commits the current settings to the active scope.
|
||||||
|
|
@ -131,50 +136,47 @@ public:
|
||||||
*/
|
*/
|
||||||
void commit();
|
void commit();
|
||||||
|
|
||||||
void determineScope();
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Getters
|
* 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 isActiveScopeWritable() const;
|
||||||
bool isActiveScopeSystem() const;
|
bool isActiveScopeSystem() const;
|
||||||
const QString &screenName() const;
|
|
||||||
int port() const;
|
|
||||||
const QString &networkInterface() const;
|
|
||||||
int logLevel() const;
|
int logLevel() const;
|
||||||
bool logToFile() const;
|
|
||||||
const QString &logFilename() const;
|
|
||||||
QString logLevelText() const;
|
|
||||||
ProcessMode processMode() const;
|
|
||||||
bool wizardShouldRun() const;
|
bool wizardShouldRun() const;
|
||||||
bool startedBefore() const;
|
bool startedBefore() const;
|
||||||
QString coreServerName() const;
|
|
||||||
QString coreClientName() const;
|
|
||||||
QString logDir() const;
|
QString logDir() const;
|
||||||
void persistLogDir() const;
|
|
||||||
ElevateMode elevateMode() const;
|
|
||||||
bool autoHide() const;
|
bool autoHide() const;
|
||||||
bool invertScrollDirection() const;
|
|
||||||
unsigned long long licenseNextCheck() const;
|
unsigned long long licenseNextCheck() const;
|
||||||
bool languageSync() const;
|
|
||||||
bool preventSleep() const;
|
|
||||||
bool invertConnection() const;
|
|
||||||
bool serverGroupChecked() const;
|
bool serverGroupChecked() const;
|
||||||
bool useExternalConfig() const;
|
|
||||||
const QString &configFile() const;
|
|
||||||
bool useInternalConfig() const;
|
bool useInternalConfig() const;
|
||||||
bool clientGroupChecked() const;
|
bool clientGroupChecked() const;
|
||||||
QString serverHostname() const;
|
QString serverHostname() const;
|
||||||
QString lastVersion() const;
|
QString lastVersion() const;
|
||||||
bool enableService() const;
|
bool enableService() const;
|
||||||
bool closeToTray() const;
|
bool closeToTray() const;
|
||||||
QString serialKey() const;
|
|
||||||
bool activationHasRun() const;
|
bool activationHasRun() const;
|
||||||
bool tlsEnabled() const override;
|
|
||||||
QString tlsCertPath() const override;
|
|
||||||
QString tlsKeyLength() const override;
|
|
||||||
std::optional<QSize> mainWindowSize() const;
|
std::optional<QSize> mainWindowSize() const;
|
||||||
std::optional<QPoint> mainWindowPosition() const;
|
std::optional<QPoint> mainWindowPosition() const;
|
||||||
bool showDevThanks() const;
|
bool showDevThanks() const;
|
||||||
|
|
@ -184,6 +186,7 @@ public:
|
||||||
* Setters
|
* Setters
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
void setActivationHasRun(bool value);
|
||||||
void setScreenName(const QString &s);
|
void setScreenName(const QString &s);
|
||||||
void setPort(int i);
|
void setPort(int i);
|
||||||
void setNetworkInterface(const QString &s);
|
void setNetworkInterface(const QString &s);
|
||||||
|
|
@ -211,7 +214,7 @@ public:
|
||||||
void setEnableService(bool enabled);
|
void setEnableService(bool enabled);
|
||||||
void setCloseToTray(bool minimize);
|
void setCloseToTray(bool minimize);
|
||||||
void setTlsCertPath(const QString &path);
|
void setTlsCertPath(const QString &path);
|
||||||
void setTlsKeyLength(const QString &length);
|
void setTlsKeyLength(int length);
|
||||||
void setInvertConnection(bool value);
|
void setInvertConnection(bool value);
|
||||||
void setMainWindowSize(const QSize &size);
|
void setMainWindowSize(const QSize &size);
|
||||||
void setMainWindowPosition(const QPoint &position);
|
void setMainWindowPosition(const QPoint &position);
|
||||||
|
|
@ -274,30 +277,30 @@ private:
|
||||||
|
|
||||||
/// @brief This method loads config from specified scope
|
/// @brief This method loads config from specified scope
|
||||||
/// @param [in] scope which should be loaded.
|
/// @param [in] scope which should be loaded.
|
||||||
void loadScope(synergy::gui::ConfigScopes::Scope scope);
|
void loadScope(IConfigScopes::Scope scope);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Gets a TLS certificate path based on the user's profile dir.
|
* @brief Gets a TLS certificate path based on the user's profile dir.
|
||||||
*/
|
*/
|
||||||
QString defaultTlsCertPath() const;
|
QString defaultTlsCertPath() const;
|
||||||
|
|
||||||
synergy::gui::IConfigScopes &m_scopes;
|
synergy::gui::IConfigScopes &m_Scopes;
|
||||||
std::shared_ptr<Deps> m_pDeps;
|
std::shared_ptr<Deps> m_pDeps;
|
||||||
QString m_ScreenName;
|
QString m_ScreenName;
|
||||||
int m_Port = 24800;
|
int m_Port = 24800;
|
||||||
QString m_Interface = "";
|
QString m_Interface = "";
|
||||||
int m_LogLevel = 0;
|
int m_LogLevel = 0;
|
||||||
bool m_LogToFile = false;
|
bool m_LogToFile = false;
|
||||||
QString m_LogFilename = logDir() + kDefaultLogFile;
|
QString m_LogFilename = logDir() + synergy::gui::kDefaultLogFile;
|
||||||
int m_WizardLastRun = 0;
|
int m_WizardLastRun = 0;
|
||||||
bool m_StartedBefore = false;
|
bool m_StartedBefore = false;
|
||||||
ElevateMode m_ElevateMode = kDefaultElevateMode;
|
ElevateMode m_ElevateMode = synergy::gui::kDefaultElevateMode;
|
||||||
QString m_ActivateEmail = "";
|
QString m_ActivateEmail = "";
|
||||||
bool m_TlsEnabled = true;
|
bool m_TlsEnabled = true;
|
||||||
bool m_AutoHide = false;
|
bool m_AutoHide = false;
|
||||||
QString m_SerialKey = "";
|
QString m_SerialKey = "";
|
||||||
QString m_LastVersion = "";
|
QString m_LastVersion = "";
|
||||||
unsigned long long m_licenseNextCheck = 0;
|
unsigned long long m_LicenseNextCheck = 0;
|
||||||
bool m_ActivationHasRun = false;
|
bool m_ActivationHasRun = false;
|
||||||
bool m_InvertScrollDirection = false;
|
bool m_InvertScrollDirection = false;
|
||||||
bool m_LanguageSync = true;
|
bool m_LanguageSync = true;
|
||||||
|
|
@ -309,13 +312,14 @@ private:
|
||||||
bool m_UseInternalConfig = false;
|
bool m_UseInternalConfig = false;
|
||||||
bool m_ClientGroupChecked = false;
|
bool m_ClientGroupChecked = false;
|
||||||
QString m_ServerHostname = "";
|
QString m_ServerHostname = "";
|
||||||
bool m_EnableService = kDefaultProcessMode == ProcessMode::kService;
|
bool m_EnableService =
|
||||||
|
synergy::gui::kDefaultProcessMode == ProcessMode::kService;
|
||||||
bool m_CloseToTray = true;
|
bool m_CloseToTray = true;
|
||||||
QString m_TlsCertPath = defaultTlsCertPath();
|
QString m_TlsCertPath = defaultTlsCertPath();
|
||||||
QString m_TlsKeyLength = "2048";
|
int m_TlsKeyLength = synergy::gui::kDefaultTlsKeyLength;
|
||||||
std::optional<QSize> m_MainWindowSize;
|
std::optional<QSize> m_MainWindowSize;
|
||||||
std::optional<QPoint> m_MainWindowPosition;
|
std::optional<QPoint> m_MainWindowPosition;
|
||||||
bool m_ShowDevThanks = kDefaultShowDevThanks;
|
bool m_ShowDevThanks = synergy::gui::kDefaultShowDevThanks;
|
||||||
bool m_LoadFromSystemScope = false;
|
bool m_LoadFromSystemScope = false;
|
||||||
bool m_ShowCloseReminder = true;
|
bool m_ShowCloseReminder = true;
|
||||||
|
|
||||||
|
|
@ -18,20 +18,23 @@
|
||||||
#include "ConfigScopes.h"
|
#include "ConfigScopes.h"
|
||||||
|
|
||||||
#include <QCoreApplication>
|
#include <QCoreApplication>
|
||||||
|
#include <QDir>
|
||||||
#include <QFile>
|
#include <QFile>
|
||||||
#include <cassert>
|
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
|
||||||
const auto kSystemConfigFilename = "SystemConfig.ini";
|
const auto kSystemConfigFilename = "SystemConfig.ini";
|
||||||
|
|
||||||
|
#if defined(Q_OS_UNIX)
|
||||||
const auto kUnixSystemConfigPath = "/usr/local/etc/symless/";
|
const auto kUnixSystemConfigPath = "/usr/local/etc/symless/";
|
||||||
|
#endif
|
||||||
|
|
||||||
namespace synergy::gui {
|
namespace synergy::gui {
|
||||||
|
|
||||||
QString getSystemSettingPath() {
|
QString getSystemSettingPath() {
|
||||||
const QString settingFilename(kSystemConfigFilename);
|
const QString settingFilename(kSystemConfigFilename);
|
||||||
#if defined(Q_OS_WIN)
|
#if defined(Q_OS_WIN)
|
||||||
return QCoreApplication::applicationDirPath() + "\\";
|
return QCoreApplication::applicationDirPath() + QDir::separator();
|
||||||
#elif defined(Q_OS_DARWIN)
|
#elif defined(Q_OS_MAC)
|
||||||
// it would be nice to use /Library dir, but qt has no elevate system.
|
// it would be nice to use /Library dir, but qt has no elevate system.
|
||||||
return kUnixSystemConfigPath + settingFilename;
|
return kUnixSystemConfigPath + settingFilename;
|
||||||
#elif defined(Q_OS_LINUX)
|
#elif defined(Q_OS_LINUX)
|
||||||
|
|
@ -32,7 +32,7 @@ class ConfigScopes : public QObject, public IConfigScopes {
|
||||||
|
|
||||||
public:
|
public:
|
||||||
explicit ConfigScopes();
|
explicit ConfigScopes();
|
||||||
virtual ~ConfigScopes() = default;
|
~ConfigScopes() override = default;
|
||||||
|
|
||||||
void signalReady() override;
|
void signalReady() override;
|
||||||
void save() 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
|
* 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
|
* This package is free software; you can redistribute it and/or
|
||||||
* modify it under the terms of the GNU General Public License
|
* modify it under the terms of the GNU General Public License
|
||||||
|
|
@ -19,23 +19,36 @@
|
||||||
|
|
||||||
#include <QString>
|
#include <QString>
|
||||||
|
|
||||||
|
namespace synergy::gui {
|
||||||
|
|
||||||
const auto kAppName = "Synergy";
|
const auto kAppName = "Synergy";
|
||||||
|
|
||||||
// TODO: change to `com.symless`. we'll need to gracefully import old settings,
|
// TODO: change to `com.symless`. we'll need to gracefully import old settings,
|
||||||
// since qt uses this on some platforms when saving settings.
|
// since qt uses this on some platforms when saving settings.
|
||||||
const auto kAppDomain = "https://symless.com";
|
const auto kAppDomain = "https://symless.com";
|
||||||
|
|
||||||
|
const int kDebugLogLevel = 1;
|
||||||
|
|
||||||
#ifdef SYNERGY_PRODUCT_NAME
|
#ifdef SYNERGY_PRODUCT_NAME
|
||||||
const QString kProductName = SYNERGY_PRODUCT_NAME;
|
const QString kProductName = SYNERGY_PRODUCT_NAME;
|
||||||
#else
|
#else
|
||||||
const QString kProductName;
|
const QString kProductName;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef SYNERGY_ENABLE_LICENSING
|
#ifdef SYNERGY_LICENSED_PRODUCT
|
||||||
const bool kLicensingEnabled = true;
|
const bool kLicensedProduct = true;
|
||||||
#else
|
#else
|
||||||
const bool kLicensingEnabled = false;
|
const bool kLicensedProduct = false;
|
||||||
#endif // SYNERGY_ENABLE_LICENSING
|
#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 kLinkBuy = R"(<a href="%1" style="color: %2">Buy now</a>)";
|
||||||
const auto kLinkRenew = R"(<a href="%1" style="color: %2">Renew now</a>)";
|
const auto kLinkRenew = R"(<a href="%1" style="color: %2">Renew now</a>)";
|
||||||
|
|
@ -43,7 +56,7 @@ const auto kLinkDownload = R"(<a href="%1" style="color: %2">Download now</a>)";
|
||||||
|
|
||||||
const auto kUrlSourceQuery = "source=gui";
|
const auto kUrlSourceQuery = "source=gui";
|
||||||
const auto kUrlWebsite = "https://symless.com";
|
const auto kUrlWebsite = "https://symless.com";
|
||||||
const auto kUrlContribute = "https://github.com/symless/synergy-core";
|
const auto kUrlGitHub = "https://github.com/symless/synergy-core";
|
||||||
const auto kUrlGnomeTrayFix =
|
const auto kUrlGnomeTrayFix =
|
||||||
"https://extensions.gnome.org/extension/2890/tray-icons-reloaded/";
|
"https://extensions.gnome.org/extension/2890/tray-icons-reloaded/";
|
||||||
const auto kUrlProduct = QString("%1/synergy").arg(kUrlWebsite);
|
const auto kUrlProduct = QString("%1/synergy").arg(kUrlWebsite);
|
||||||
|
|
@ -54,3 +67,6 @@ const auto kUrlContact =
|
||||||
const auto kUrlHelp = QString("%1/help?%2").arg(kUrlProduct, kUrlSourceQuery);
|
const auto kUrlHelp = QString("%1/help?%2").arg(kUrlProduct, kUrlSourceQuery);
|
||||||
const auto kUrlDownload =
|
const auto kUrlDownload =
|
||||||
QString("%1/download?%2").arg(kUrlProduct, kUrlSourceQuery);
|
QString("%1/download?%2").arg(kUrlProduct, kUrlSourceQuery);
|
||||||
|
const auto kUrlBugReport = QString("%1/issues").arg(kUrlGitHub);
|
||||||
|
|
||||||
|
} // namespace synergy::gui
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,6 @@
|
||||||
/*
|
/*
|
||||||
* synergy -- mouse and keyboard sharing utility
|
* synergy -- mouse and keyboard sharing utility
|
||||||
* Copyright (C) 2012-2021 Symless Ltd.
|
* Copyright (C) 2021 Symless Ltd.
|
||||||
* Copyright (C) 2008 Volker Lanz (vl@fidra.de)
|
|
||||||
*
|
*
|
||||||
* This package is free software; you can redistribute it and/or
|
* This package is free software; you can redistribute it and/or
|
||||||
* modify it under the terms of the GNU General Public License
|
* modify it under the terms of the GNU General Public License
|
||||||
|
|
@ -18,12 +17,14 @@
|
||||||
|
|
||||||
#include "ClientConnection.h"
|
#include "ClientConnection.h"
|
||||||
|
|
||||||
#include "MainWindow.h"
|
|
||||||
|
|
||||||
#include <QHostAddress>
|
#include <QHostAddress>
|
||||||
#include <QMessageBox>
|
#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) {
|
void ClientConnection::update(const QString &line) {
|
||||||
if (m_checkConnection && checkMainWindow()) {
|
if (m_checkConnection && checkMainWindow()) {
|
||||||
|
|
@ -59,13 +60,13 @@ QString ClientConnection::getMessage(const QString &line) const {
|
||||||
message =
|
message =
|
||||||
QObject::tr("Connection failed.\nYou can’t name 2 computers the same.");
|
QObject::tr("Connection failed.\nYou can’t name 2 computers the same.");
|
||||||
} else {
|
} else {
|
||||||
QHostAddress address(m_parent.appConfig().serverHostname());
|
QHostAddress address(m_appConfig.serverHostname());
|
||||||
if (address.isNull()) {
|
if (address.isNull()) {
|
||||||
message =
|
message =
|
||||||
QObject::tr(
|
QObject::tr(
|
||||||
"We can’t connect to the server \"%1\" try to connect using the "
|
"We can’t connect to the server \"%1\" try to connect using the "
|
||||||
"server IP address and check your firewall settings.")
|
"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) {
|
void ClientConnection::setCheckConnection(bool checkConnection) {
|
||||||
m_checkConnection = checkConnection;
|
m_checkConnection = checkConnection;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
} // namespace synergy::gui
|
||||||
|
|
@ -1,7 +1,6 @@
|
||||||
/*
|
/*
|
||||||
* synergy -- mouse and keyboard sharing utility
|
* synergy -- mouse and keyboard sharing utility
|
||||||
* Copyright (C) 2012-2021 Symless Ltd.
|
* Copyright (C) 2021 Symless Ltd.
|
||||||
* Copyright (C) 2008 Volker Lanz (vl@fidra.de)
|
|
||||||
*
|
*
|
||||||
* This package is free software; you can redistribute it and/or
|
* This package is free software; you can redistribute it and/or
|
||||||
* modify it under the terms of the GNU General Public License
|
* modify it under the terms of the GNU General Public License
|
||||||
|
|
@ -16,19 +15,18 @@
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifndef CLIENTCONNECTION_H
|
#pragma once
|
||||||
#define CLIENTCONNECTION_H
|
|
||||||
|
#include "gui/config/AppConfig.h"
|
||||||
|
|
||||||
#include <QString>
|
#include <QString>
|
||||||
|
#include <QWidget>
|
||||||
|
|
||||||
class MainWindow;
|
namespace synergy::gui {
|
||||||
|
|
||||||
class ClientConnection {
|
class ClientConnection {
|
||||||
MainWindow &m_parent;
|
|
||||||
bool m_checkConnection = false;
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
explicit ClientConnection(MainWindow &parent);
|
explicit ClientConnection(QWidget &parent, AppConfig &appConfig);
|
||||||
void update(const QString &line);
|
void update(const QString &line);
|
||||||
void setCheckConnection(bool checkConnection);
|
void setCheckConnection(bool checkConnection);
|
||||||
|
|
||||||
|
|
@ -36,6 +34,10 @@ private:
|
||||||
QString getMessage(const QString &line) const;
|
QString getMessage(const QString &line) const;
|
||||||
bool checkMainWindow();
|
bool checkMainWindow();
|
||||||
void showMessage(const QString &message) const;
|
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()) {
|
if (process.waitForFinished()) {
|
||||||
output =
|
output = process.readAllStandardOutput().trimmed();
|
||||||
QString::fromLocal8Bit(process.readAllStandardOutput().trimmed());
|
|
||||||
error = process.readAllStandardError().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/>.
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "gui/CoreInterface.h"
|
#include "gui/core/CoreTool.h"
|
||||||
|
|
||||||
#include "CommandProcess.h"
|
#include "CommandProcess.h"
|
||||||
|
|
||||||
|
|
@ -32,28 +32,27 @@ static const char kSerialKeyFilename[] = "Synergy.subkey";
|
||||||
static const char kSerialKeyFilename[] = ".synergy.subkey";
|
static const char kSerialKeyFilename[] = ".synergy.subkey";
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
QString CoreInterface::getProfileDir() const {
|
QString CoreTool::getProfileDir() const {
|
||||||
QStringList args("--get-profile-dir");
|
QStringList args("--get-profile-dir");
|
||||||
return QDir::cleanPath(run(args));
|
return QDir::cleanPath(run(args));
|
||||||
}
|
}
|
||||||
|
|
||||||
QString CoreInterface::getInstalledDir() const {
|
QString CoreTool::getInstalledDir() const {
|
||||||
QStringList args("--get-installed-dir");
|
QStringList args("--get-installed-dir");
|
||||||
return QDir::cleanPath(run(args));
|
return QDir::cleanPath(run(args));
|
||||||
}
|
}
|
||||||
|
|
||||||
QString CoreInterface::getArch() const {
|
QString CoreTool::getArch() const {
|
||||||
QStringList args("--get-arch");
|
QStringList args("--get-arch");
|
||||||
return run(args);
|
return run(args);
|
||||||
}
|
}
|
||||||
|
|
||||||
QString CoreInterface::getSerialKeyFilePath() const {
|
QString CoreTool::getSerialKeyFilePath() const {
|
||||||
auto filename = getProfileDir() + QDir::separator() + kSerialKeyFilename;
|
auto filename = getProfileDir() + QDir::separator() + kSerialKeyFilename;
|
||||||
return QDir::cleanPath(filename);
|
return QDir::cleanPath(filename);
|
||||||
}
|
}
|
||||||
|
|
||||||
QString
|
QString CoreTool::run(const QStringList &args, const QString &input) const {
|
||||||
CoreInterface::run(const QStringList &args, const QString &input) const {
|
|
||||||
QString program(QCoreApplication::applicationDirPath() + "/" + kCoreBinary);
|
QString program(QCoreApplication::applicationDirPath() + "/" + kCoreBinary);
|
||||||
|
|
||||||
CommandProcess commandProcess(program, args, input);
|
CommandProcess commandProcess(program, args, input);
|
||||||
|
|
@ -19,7 +19,7 @@
|
||||||
|
|
||||||
#include <QString>
|
#include <QString>
|
||||||
|
|
||||||
class CoreInterface {
|
class CoreTool {
|
||||||
public:
|
public:
|
||||||
QString getProfileDir() const;
|
QString getProfileDir() const;
|
||||||
QString getInstalledDir() 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
|
* synergy -- mouse and keyboard sharing utility
|
||||||
* Copyright (C) 2012-2021 Symless Ltd.
|
* Copyright (C) 2021 Symless Ltd.
|
||||||
* Copyright (C) 2008 Volker Lanz (vl@fidra.de)
|
|
||||||
*
|
*
|
||||||
* This package is free software; you can redistribute it and/or
|
* This package is free software; you can redistribute it and/or
|
||||||
* modify it under the terms of the GNU General Public License
|
* modify it under the terms of the GNU General Public License
|
||||||
|
|
@ -16,26 +15,36 @@
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifndef SERVERCONNECTION_H
|
#pragma once
|
||||||
#define SERVERCONNECTION_H
|
|
||||||
|
|
||||||
#include <QString>
|
#include <QString>
|
||||||
#include <QStringList>
|
#include <QStringList>
|
||||||
|
|
||||||
class MainWindow;
|
#include "gui/config/AppConfig.h"
|
||||||
|
#include "gui/config/IServerConfig.h"
|
||||||
|
|
||||||
class ServerConnection {
|
namespace synergy::gui {
|
||||||
MainWindow &m_parent;
|
|
||||||
QStringList m_ignoredClients;
|
class ServerConnection : public QObject {
|
||||||
|
Q_OBJECT
|
||||||
|
using IServerConfig = synergy::gui::IServerConfig;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
explicit ServerConnection(MainWindow &parent);
|
explicit ServerConnection(
|
||||||
|
QWidget &parent, AppConfig &appConfig, IServerConfig &serverConfig);
|
||||||
void update(const QString &line);
|
void update(const QString &line);
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void configureClient(const QString &clientName);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void addClient(const QString &clientName);
|
void addClient(const QString &clientName);
|
||||||
void configureClient(const QString &clientName);
|
|
||||||
bool checkMainWindow();
|
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
|
* synergy -- mouse and keyboard sharing utility
|
||||||
* Copyright (C) 2012-2021 Symless Ltd.
|
* Copyright (C) 2021 Symless Ltd.
|
||||||
* Copyright (C) 2008 Volker Lanz (vl@fidra.de)
|
|
||||||
*
|
*
|
||||||
* This package is free software; you can redistribute it and/or
|
* This package is free software; you can redistribute it and/or
|
||||||
* modify it under the terms of the GNU General Public License
|
* modify it under the terms of the GNU General Public License
|
||||||
|
|
@ -15,8 +14,11 @@
|
||||||
* You should have received a copy of the GNU General Public License
|
* You should have received a copy of the GNU General Public License
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "ServerMessage.h"
|
#include "ServerMessage.h"
|
||||||
|
|
||||||
|
namespace synergy::gui {
|
||||||
|
|
||||||
ServerMessage::ServerMessage(const QString &message)
|
ServerMessage::ServerMessage(const QString &message)
|
||||||
: m_message(message),
|
: m_message(message),
|
||||||
m_clienName(parseClientName(message)) {}
|
m_clienName(parseClientName(message)) {}
|
||||||
|
|
@ -50,3 +52,5 @@ QString ServerMessage::parseClientName(const QString &line) const {
|
||||||
|
|
||||||
return clientName;
|
return clientName;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
} // namespace synergy::gui
|
||||||
|
|
@ -1,7 +1,6 @@
|
||||||
/*
|
/*
|
||||||
* synergy -- mouse and keyboard sharing utility
|
* synergy -- mouse and keyboard sharing utility
|
||||||
* Copyright (C) 2012-2021 Symless Ltd.
|
* Copyright (C) 2021 Symless Ltd.
|
||||||
* Copyright (C) 2008 Volker Lanz (vl@fidra.de)
|
|
||||||
*
|
*
|
||||||
* This package is free software; you can redistribute it and/or
|
* This package is free software; you can redistribute it and/or
|
||||||
* modify it under the terms of the GNU General Public License
|
* modify it under the terms of the GNU General Public License
|
||||||
|
|
@ -16,11 +15,12 @@
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifndef SERVERMESSAGE_H
|
#pragma once
|
||||||
#define SERVERMESSAGE_H
|
|
||||||
|
|
||||||
#include <QString>
|
#include <QString>
|
||||||
|
|
||||||
|
namespace synergy::gui {
|
||||||
|
|
||||||
class ServerMessage {
|
class ServerMessage {
|
||||||
QString m_message;
|
QString m_message;
|
||||||
QString m_clienName;
|
QString m_clienName;
|
||||||
|
|
@ -39,4 +39,4 @@ private:
|
||||||
QString parseClientName(const QString &line) const;
|
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/>.
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#pragma once
|
// empty .cpp for qt moc
|
||||||
|
|
||||||
#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
|
|
||||||
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 "IpcReader.h"
|
||||||
|
|
||||||
|
#include "Logger.h"
|
||||||
|
#include "byte_utils.h"
|
||||||
#include "global/Ipc.h"
|
#include "global/Ipc.h"
|
||||||
|
|
||||||
#include <QByteArray>
|
#include <QByteArray>
|
||||||
#include <QMutex>
|
#include <QMutex>
|
||||||
#include <QTcpSocket>
|
#include <QTcpSocket>
|
||||||
#include <iostream>
|
|
||||||
|
using namespace synergy::gui;
|
||||||
|
|
||||||
IpcReader::IpcReader(QTcpSocket *socket) : m_Socket(socket) {}
|
IpcReader::IpcReader(QTcpSocket *socket) : m_Socket(socket) {}
|
||||||
|
|
||||||
IpcReader::~IpcReader() {}
|
void IpcReader::start() const {
|
||||||
|
connect(
|
||||||
void IpcReader::start() {
|
m_Socket, &QTcpSocket::readyRead, this, &IpcReader::onSocketReadyRead);
|
||||||
connect(m_Socket, SIGNAL(readyRead()), this, SLOT(read()));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void IpcReader::stop() {
|
void IpcReader::stop() const {
|
||||||
disconnect(m_Socket, SIGNAL(readyRead()), this, SLOT(read()));
|
disconnect(
|
||||||
|
m_Socket, &QTcpSocket::readyRead, this, &IpcReader::onSocketReadyRead);
|
||||||
}
|
}
|
||||||
|
|
||||||
void IpcReader::read() {
|
void IpcReader::onSocketReadyRead() {
|
||||||
QMutexLocker locker(&m_Mutex);
|
QMutexLocker locker(&m_Mutex);
|
||||||
std::cout << "ready read" << std::endl;
|
logVerbose("ready read");
|
||||||
|
|
||||||
while (m_Socket->bytesAvailable()) {
|
while (m_Socket->bytesAvailable()) {
|
||||||
std::cout << "bytes available" << std::endl;
|
logVerbose("bytes available");
|
||||||
|
|
||||||
char codeBuf[5];
|
char codeBuf[5];
|
||||||
readStream(codeBuf, 4);
|
readStream(codeBuf, 4);
|
||||||
codeBuf[4] = 0;
|
codeBuf[4] = 0;
|
||||||
std::cout << "ipc read: " << codeBuf << std::endl;
|
logVerbose(QString("ipc read: %1").arg(codeBuf));
|
||||||
|
|
||||||
if (memcmp(codeBuf, kIpcMsgLogLine, 4) == 0) {
|
if (memcmp(codeBuf, kIpcMsgLogLine, 4) == 0) {
|
||||||
std::cout << "reading log line" << std::endl;
|
logVerbose("reading log line");
|
||||||
|
|
||||||
char lenBuf[4];
|
char lenBuf[4];
|
||||||
readStream(lenBuf, 4);
|
readStream(lenBuf, 4);
|
||||||
int len = bytesToInt(lenBuf, 4);
|
int len = bytesToInt(lenBuf, 4);
|
||||||
|
|
||||||
char *data = new char[len];
|
std::vector<char> dataBuf(len);
|
||||||
readStream(data, len);
|
readStream(dataBuf.data(), len);
|
||||||
QString line = QString::fromUtf8(data, len);
|
QString text = QString::fromUtf8(dataBuf.data(), len);
|
||||||
delete[] data;
|
|
||||||
|
|
||||||
readLogLine(line);
|
emit read(text);
|
||||||
|
} else if (memcmp(codeBuf, kIpcMsgHelloBack, 4) == 0) {
|
||||||
|
logVerbose("reading hello back");
|
||||||
|
emit helloBack();
|
||||||
} else {
|
} else {
|
||||||
std::cerr << "aborting, message invalid" << std::endl;
|
qCritical("aborting ipc read, message invalid");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
std::cout << "read done" << std::endl;
|
logVerbose("read done");
|
||||||
}
|
}
|
||||||
|
|
||||||
bool IpcReader::readStream(char *buffer, int length) {
|
bool IpcReader::readStream(char *buffer, int length) {
|
||||||
std::cout << "reading stream" << std::endl;
|
logVerbose("reading stream");
|
||||||
|
|
||||||
int read = 0;
|
int read = 0;
|
||||||
while (read < length) {
|
while (read < length) {
|
||||||
int ask = length - read;
|
int ask = length - read;
|
||||||
if (m_Socket->bytesAvailable() < ask) {
|
if (m_Socket->bytesAvailable() < ask) {
|
||||||
std::cout << "buffer too short, waiting" << std::endl;
|
logVerbose("buffer too short, waiting");
|
||||||
m_Socket->waitForReadyRead(-1);
|
m_Socket->waitForReadyRead(-1);
|
||||||
}
|
}
|
||||||
|
|
||||||
int got = m_Socket->read(buffer, ask);
|
auto got = m_Socket->read(buffer, ask);
|
||||||
read += got;
|
read += got;
|
||||||
|
|
||||||
std::cout << "> ask=" << ask << " got=" << got << " read=" << read
|
logVerbose(QString("ask=%1 got=%2 read=%3").arg(ask).arg(got).arg(read));
|
||||||
<< std::endl;
|
|
||||||
|
|
||||||
if (got == -1) {
|
if (got == -1) {
|
||||||
std::cout << "socket ended, aborting" << std::endl;
|
logVerbose("socket ended, aborting");
|
||||||
return false;
|
return false;
|
||||||
} else if (length - read > 0) {
|
} 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;
|
buffer += got;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return true;
|
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;
|
Q_OBJECT;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
IpcReader(QTcpSocket *socket);
|
explicit IpcReader(QTcpSocket *socket);
|
||||||
virtual ~IpcReader();
|
~IpcReader() override = default;
|
||||||
void start();
|
void start() const;
|
||||||
void stop();
|
void stop() const;
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void readLogLine(const QString &text);
|
void read(const QString &text);
|
||||||
|
void helloBack();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
bool readStream(char *buffer, int length);
|
bool readStream(char *buffer, int length);
|
||||||
int bytesToInt(const char *buffer, int size);
|
|
||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
void read();
|
void onSocketReadyRead();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
QTcpSocket *m_Socket;
|
QTcpSocket *m_Socket;
|
||||||
|
|
@ -28,7 +28,7 @@ public:
|
||||||
}
|
}
|
||||||
virtual ~QDataStreamProxy() = default;
|
virtual ~QDataStreamProxy() = default;
|
||||||
|
|
||||||
virtual qint64 writeRawData(const char *data, qint64 len) {
|
virtual qint64 writeRawData(const char *data, int len) {
|
||||||
assert(m_Stream);
|
assert(m_Stream);
|
||||||
return m_Stream->writeRawData(data, len);
|
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 <QDataStream>
|
||||||
#include <QObject>
|
#include <QObject>
|
||||||
#include <QTcpSocket>
|
#include <QTcpSocket>
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
#include "ElevateMode.h"
|
#include "IpcReader.h"
|
||||||
#include "QDataStreamProxy.h"
|
#include "QDataStreamProxy.h"
|
||||||
|
#include "gui/config/ElevateMode.h"
|
||||||
|
#include "gui/ipc/IQIpcClient.h"
|
||||||
|
|
||||||
class IpcReader;
|
class IpcReader;
|
||||||
|
|
||||||
class QIpcClient : public QObject {
|
class QIpcClient : public synergy::gui::ipc::IQIpcClient {
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
public:
|
public:
|
||||||
using StreamProvider = std::function<std::shared_ptr<QDataStreamProxy>()>;
|
using StreamProvider = std::function<std::shared_ptr<QDataStreamProxy>()>;
|
||||||
|
|
||||||
explicit QIpcClient(const StreamProvider &streamProvider = nullptr);
|
explicit QIpcClient(const StreamProvider &streamProvider = nullptr);
|
||||||
~QIpcClient() override;
|
|
||||||
|
|
||||||
void sendHello();
|
void sendHello() const override;
|
||||||
void sendCommand(const QString &command, ElevateMode elevate);
|
void sendCommand(const QString &command, ElevateMode elevate) const override;
|
||||||
void connectToHost();
|
void connectToHost() override;
|
||||||
void disconnectFromHost();
|
void disconnectFromHost() override;
|
||||||
|
bool isConnected() const override { return m_isConnected; }
|
||||||
public slots:
|
|
||||||
void retryConnect();
|
|
||||||
|
|
||||||
private:
|
|
||||||
void intToBytes(int value, char *buffer, int size);
|
|
||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
void connected();
|
void onRetryConnect();
|
||||||
void error(QAbstractSocket::SocketError error);
|
void onSocketConnected() const;
|
||||||
void handleReadLogLine(const QString &text);
|
void onIpcReaderHelloBack();
|
||||||
|
void onSocketError(QAbstractSocket::SocketError error) const;
|
||||||
signals:
|
void onIpcReaderRead(const QString &text);
|
||||||
void readLogLine(const QString &text);
|
|
||||||
void infoMessage(const QString &text);
|
|
||||||
void errorMessage(const QString &text);
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
QTcpSocket *m_Socket;
|
std::unique_ptr<QTcpSocket> m_pSocket;
|
||||||
IpcReader *m_Reader;
|
std::unique_ptr<IpcReader> m_pReader;
|
||||||
bool m_ReaderStarted;
|
bool m_readerStarted = false;
|
||||||
bool m_Enabled;
|
StreamProvider m_streamProvider;
|
||||||
StreamProvider m_StreamProvider;
|
bool m_isConnected = false;
|
||||||
|
bool m_isConnecting = false;
|
||||||
};
|
};
|
||||||
|
|
@ -52,6 +52,6 @@ signals:
|
||||||
void invalidLicense() const;
|
void invalidLicense() const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
bool m_enabled = kLicensingEnabled;
|
bool m_enabled = synergy::gui::kEnableActivation;
|
||||||
License m_license = License::invalid();
|
License m_license = License::invalid();
|
||||||
};
|
};
|
||||||
|
|
@ -35,6 +35,7 @@ QString licenseNotice(const License &license) {
|
||||||
return subscriptionLicenseNotice(license);
|
return subscriptionLicenseNotice(license);
|
||||||
} else {
|
} else {
|
||||||
qFatal("license notice only for time limited licenses");
|
qFatal("license notice only for time limited licenses");
|
||||||
|
return "";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -17,75 +17,100 @@
|
||||||
|
|
||||||
#include "messages.h"
|
#include "messages.h"
|
||||||
|
|
||||||
|
#include "Logger.h"
|
||||||
#include "constants.h"
|
#include "constants.h"
|
||||||
#include "styles.h"
|
#include "styles.h"
|
||||||
|
|
||||||
|
#include <QAction>
|
||||||
#include <QDateTime>
|
#include <QDateTime>
|
||||||
|
#include <QDir>
|
||||||
#include <QMessageBox>
|
#include <QMessageBox>
|
||||||
#include <QTime>
|
#include <QTime>
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
namespace synergy::gui::messages {
|
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(
|
void messageHandler(
|
||||||
QtMsgType type, const QMessageLogContext &context, const QString &message) {
|
QtMsgType type, const QMessageLogContext &context, const QString &message) {
|
||||||
|
|
||||||
auto datetime = QDateTime::currentDateTime().toString("yyyy-MM-ddTHH:mm:ss");
|
Logger::instance().handleMessage(type, context, message);
|
||||||
const auto filename = QString(context.file).split("/").last();
|
|
||||||
auto function = context.function ? context.function : "";
|
|
||||||
|
|
||||||
QString typeString;
|
if (type == QtFatalMsg || type == QtCriticalMsg) {
|
||||||
auto out = stdout;
|
showErrorDialog(message, context, type);
|
||||||
switch (type) {
|
|
||||||
case QtDebugMsg:
|
|
||||||
typeString = "DEBUG";
|
|
||||||
break;
|
|
||||||
case QtInfoMsg:
|
|
||||||
typeString = "INFO";
|
|
||||||
break;
|
|
||||||
case QtWarningMsg:
|
|
||||||
typeString = "WARNING";
|
|
||||||
out = stderr;
|
|
||||||
break;
|
|
||||||
case QtCriticalMsg:
|
|
||||||
typeString = "CRITICAL";
|
|
||||||
out = stderr;
|
|
||||||
break;
|
|
||||||
case QtFatalMsg:
|
|
||||||
typeString = "FATAL";
|
|
||||||
out = stderr;
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
auto logLine = QString("[%1] %2: %3\n\t%4:%5, %6")
|
|
||||||
.arg(datetime)
|
|
||||||
.arg(typeString)
|
|
||||||
.arg(message)
|
|
||||||
.arg(filename)
|
|
||||||
.arg(context.line)
|
|
||||||
.arg(function);
|
|
||||||
|
|
||||||
auto logLineUtf = logLine.toUtf8();
|
|
||||||
auto logLine_c = logLineUtf.constData();
|
|
||||||
fprintf(out, "%s\n", logLine_c);
|
|
||||||
|
|
||||||
if (type == QtFatalMsg) {
|
if (type == QtFatalMsg) {
|
||||||
auto contextString =
|
// developers: if you hit this line in your debugger, traverse the stack
|
||||||
QString("%1:%2\n%3").arg(filename).arg(context.line).arg(function);
|
// 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.
|
||||||
QMessageBox::critical(
|
//
|
||||||
nullptr, "Fatal error",
|
// hint: if you don't want to crash, but still want to show an error
|
||||||
QString("<p>Sorry, a fatal error has occurred "
|
// message, use `qCritical()` instead of `qFatal()`. you should use
|
||||||
"and the application must now exit.</p>"
|
// fatal errors when the app is in an unrecoverable state; i.e. it cannot
|
||||||
"<p>Please "
|
// function correctly in it's current state and must be restarted.
|
||||||
R"(<a href="%1" style="color: %2">contact us</a>)"
|
|
||||||
" and copy/paste the following error:</p>"
|
|
||||||
"<pre>%3\n\n%4</pre>")
|
|
||||||
.arg(kUrlContact, kColorSecondary, message, contextString));
|
|
||||||
|
|
||||||
// developers: if you hit this line in your debugger, traverse the stack to
|
|
||||||
// find the cause of the fatal error.
|
|
||||||
// important: crash the app on fatal error to prevent the app being used in
|
|
||||||
// a broken state.
|
|
||||||
abort();
|
abort();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -143,16 +168,16 @@ void showDevThanks(QWidget *parent, const QString &productName) {
|
||||||
|
|
||||||
QMessageBox::information(
|
QMessageBox::information(
|
||||||
parent, "Thank you!",
|
parent, "Thank you!",
|
||||||
QString(
|
QString("<p>Thanks for using %1.</p>"
|
||||||
"<p>Thanks for using %1.</p>"
|
"<p>If you enjoy using this app, you can support the <br />"
|
||||||
"<p>If you enjoy using this app, you can support the developers by "
|
"developers by "
|
||||||
R"(<a href="%1" style="color: %2")>purchasing a license</a>)"
|
R"(<a href="%2" style="color: %3")>purchasing a license</a>)"
|
||||||
" or "
|
" or "
|
||||||
R"(<a href="%3" style="color: %4")>contributing code</a>.)"
|
R"(<a href="%4" style="color: %5")>contributing code</a>.)"
|
||||||
"</p>"
|
"</p>"
|
||||||
"<p>This message will only appear once.</p>")
|
"<p>This message will only appear once.</p>")
|
||||||
.arg(
|
.arg(
|
||||||
productName, kUrlPurchase, kColorSecondary, kUrlContribute,
|
productName, kUrlPurchase, kColorSecondary, kUrlGitHub,
|
||||||
kColorSecondary));
|
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>
|
#include <QString>
|
||||||
|
|
||||||
|
namespace synergy::gui {
|
||||||
|
|
||||||
const auto kColorWhite = "#ffffff";
|
const auto kColorWhite = "#ffffff";
|
||||||
const auto kColorPrimary = "#ff7c00";
|
const auto kColorPrimary = "#ff7c00";
|
||||||
const auto kColorSecondary = "#4285f4";
|
const auto kColorSecondary = "#4285f4";
|
||||||
|
|
@ -47,3 +49,5 @@ const auto kStyleNoticeLabel = //
|
||||||
QString("padding: 3px 5px; border-radius: 3px;"
|
QString("padding: 3px 5px; border-radius: 3px;"
|
||||||
"background-color: %1; color: %2")
|
"background-color: %1; color: %2")
|
||||||
.arg(kColorNotice, kColorWhite);
|
.arg(kColorNotice, kColorWhite);
|
||||||
|
|
||||||
|
} // namespace synergy::gui
|
||||||
|
|
|
||||||
|
|
@ -22,13 +22,10 @@
|
||||||
#include <QCoreApplication>
|
#include <QCoreApplication>
|
||||||
#include <QDir>
|
#include <QDir>
|
||||||
#include <QProcess>
|
#include <QProcess>
|
||||||
|
#include <optional>
|
||||||
|
|
||||||
// RSA Bit length (e.g. 1024/2048/4096)
|
|
||||||
static const char *const kCertificateKeyLength = "rsa:";
|
static const char *const kCertificateKeyLength = "rsa:";
|
||||||
|
|
||||||
// fingerprint hashing algorithm
|
|
||||||
static const char *const kCertificateHashAlgorithm = "-sha256";
|
static const char *const kCertificateHashAlgorithm = "-sha256";
|
||||||
|
|
||||||
static const char *const kCertificateLifetime = "365";
|
static const char *const kCertificateLifetime = "365";
|
||||||
static const char *const kCertificateSubjectInfo = "/CN=Synergy";
|
static const char *const kCertificateSubjectInfo = "/CN=Synergy";
|
||||||
static const char *const kCertificateFilename = "Synergy.pem";
|
static const char *const kCertificateFilename = "Synergy.pem";
|
||||||
|
|
@ -42,9 +39,10 @@ static const char *const kConfigFile = "synergy.conf";
|
||||||
static const char *const kUnixOpenSslCommand = "openssl";
|
static const char *const kUnixOpenSslCommand = "openssl";
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
namespace synergy::gui {
|
|
||||||
#if defined(Q_OS_WIN)
|
#if defined(Q_OS_WIN)
|
||||||
|
|
||||||
|
namespace synergy::gui {
|
||||||
|
|
||||||
QString openSslWindowsDir() {
|
QString openSslWindowsDir() {
|
||||||
|
|
||||||
auto appDir = QDir(QCoreApplication::applicationDirPath());
|
auto appDir = QDir(QCoreApplication::applicationDirPath());
|
||||||
|
|
@ -83,16 +81,16 @@ QString openSslWindowsBinary() {
|
||||||
return path;
|
return path;
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
||||||
} // namespace synergy::gui
|
} // namespace synergy::gui
|
||||||
|
|
||||||
using namespace synergy::gui;
|
using namespace synergy::gui;
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
TlsCertificate::TlsCertificate(QObject *parent) : QObject(parent) {
|
TlsCertificate::TlsCertificate(QObject *parent) : QObject(parent) {
|
||||||
m_ProfileDir = m_CoreInterface.getProfileDir();
|
m_profileDir = m_coreTool.getProfileDir();
|
||||||
if (m_ProfileDir.isEmpty()) {
|
if (m_profileDir.isEmpty()) {
|
||||||
emit error(tr("Failed to get profile directory."));
|
qCritical("empty profile directory result");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -108,92 +106,118 @@ bool TlsCertificate::runTool(const QStringList &args) {
|
||||||
#if defined(Q_OS_WIN)
|
#if defined(Q_OS_WIN)
|
||||||
auto openSslDir = QDir(openSslWindowsDir());
|
auto openSslDir = QDir(openSslWindowsDir());
|
||||||
auto config = QDir::cleanPath(openSslDir.filePath(kConfigFile));
|
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);
|
environment << QString("OPENSSL_CONF=%1").arg(config);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
qDebug(
|
||||||
|
"running: %s %s", qUtf8Printable(program),
|
||||||
|
qUtf8Printable(args.join(" ")));
|
||||||
|
|
||||||
QProcess process;
|
QProcess process;
|
||||||
|
|
||||||
|
for (const auto &envVar : environment) {
|
||||||
|
qDebug("setting env var %s", qUtf8Printable(envVar));
|
||||||
|
}
|
||||||
|
|
||||||
process.setEnvironment(environment);
|
process.setEnvironment(environment);
|
||||||
|
|
||||||
process.start(program, args);
|
process.start(program, args);
|
||||||
|
|
||||||
bool success = process.waitForStarted();
|
bool success = process.waitForStarted();
|
||||||
|
|
||||||
QString standardError;
|
QString stderrOutput;
|
||||||
if (success && process.waitForFinished()) {
|
if (success && process.waitForFinished()) {
|
||||||
m_ToolOutput = process.readAllStandardOutput().trimmed();
|
m_toolStdout = process.readAllStandardOutput().trimmed();
|
||||||
standardError = process.readAllStandardError().trimmed();
|
stderrOutput = process.readAllStandardError().trimmed();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (int code = process.exitCode(); !success || code != 0) {
|
if (int code = process.exitCode(); !success || code != 0) {
|
||||||
emit error(QString("SSL tool failed: %1\n\nCode: %2\nError: %3")
|
qDebug(
|
||||||
.arg(program)
|
"openssl failed with code %d: %s", code, qUtf8Printable(stderrOutput));
|
||||||
.arg(process.exitCode())
|
|
||||||
.arg(standardError.isEmpty() ? "Unknown" : standardError));
|
qCritical(
|
||||||
|
"failed to generate TLS certificate:\n\n%s",
|
||||||
|
qUtf8Printable(stderrOutput));
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void TlsCertificate::generateCertificate(
|
bool TlsCertificate::generateCertificate(const QString &path, int keyLength) {
|
||||||
const QString &path, const QString &keyLength, bool forceGen) {
|
|
||||||
QString sslDirPath =
|
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(sslDirPath)
|
||||||
.arg(QDir::separator())
|
.arg(QDir::separator())
|
||||||
.arg(kCertificateFilename);
|
.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);
|
||||||
|
|
||||||
if (QFile file(pathToUse); !file.exists() || forceGen) {
|
qDebug("generating tls certificate: %s", qUtf8Printable(pathToUse));
|
||||||
QStringList arguments;
|
|
||||||
|
|
||||||
// self signed certificate
|
QStringList arguments;
|
||||||
arguments.append("req");
|
|
||||||
arguments.append("-x509");
|
|
||||||
arguments.append("-nodes");
|
|
||||||
|
|
||||||
// valide duration
|
// self signed certificate
|
||||||
arguments.append("-days");
|
arguments.append("req");
|
||||||
arguments.append(kCertificateLifetime);
|
arguments.append("-x509");
|
||||||
|
arguments.append("-nodes");
|
||||||
|
|
||||||
// subject information
|
// valide duration
|
||||||
arguments.append("-subj");
|
arguments.append("-days");
|
||||||
|
arguments.append(kCertificateLifetime);
|
||||||
|
|
||||||
QString subInfo(kCertificateSubjectInfo);
|
// subject information
|
||||||
arguments.append(subInfo);
|
arguments.append("-subj");
|
||||||
|
|
||||||
// private key
|
QString subInfo(kCertificateSubjectInfo);
|
||||||
arguments.append("-newkey");
|
arguments.append(subInfo);
|
||||||
arguments.append(keySize);
|
|
||||||
|
|
||||||
if (QDir sslDir(sslDirPath); !sslDir.exists()) {
|
// private key
|
||||||
sslDir.mkpath(".");
|
arguments.append("-newkey");
|
||||||
}
|
arguments.append(keySize);
|
||||||
|
|
||||||
// key output filename
|
if (QDir sslDir(sslDirPath); !sslDir.exists()) {
|
||||||
arguments.append("-keyout");
|
sslDir.mkpath(".");
|
||||||
arguments.append(pathToUse);
|
|
||||||
|
|
||||||
// certificate output filename
|
|
||||||
arguments.append("-out");
|
|
||||||
arguments.append(pathToUse);
|
|
||||||
|
|
||||||
if (!runTool(arguments)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
generateFingerprint(pathToUse);
|
|
||||||
emit info(tr("SSL certificate generated."));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
emit generateFinished();
|
// key output filename
|
||||||
|
arguments.append("-keyout");
|
||||||
|
arguments.append(pathToUse);
|
||||||
|
|
||||||
|
// certificate output filename
|
||||||
|
arguments.append("-out");
|
||||||
|
arguments.append(pathToUse);
|
||||||
|
|
||||||
|
if (runTool(arguments)) {
|
||||||
|
qDebug("tls certificate generated");
|
||||||
|
|
||||||
|
return generateFingerprint(pathToUse);
|
||||||
|
} else {
|
||||||
|
qCritical("failed to generate tls certificate");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void TlsCertificate::generateFingerprint(const QString &certificateFilename) {
|
bool TlsCertificate::generateFingerprint(const QString &certificateFilename) {
|
||||||
|
qDebug("generating tls fingerprint");
|
||||||
|
|
||||||
QStringList arguments;
|
QStringList arguments;
|
||||||
arguments.append("x509");
|
arguments.append("x509");
|
||||||
arguments.append("-fingerprint");
|
arguments.append("-fingerprint");
|
||||||
|
|
@ -203,23 +227,26 @@ void TlsCertificate::generateFingerprint(const QString &certificateFilename) {
|
||||||
arguments.append(certificateFilename);
|
arguments.append(certificateFilename);
|
||||||
|
|
||||||
if (!runTool(arguments)) {
|
if (!runTool(arguments)) {
|
||||||
return;
|
qCritical("failed to generate tls fingerprint");
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// find the fingerprint from the tool output
|
// find the fingerprint from the tool output
|
||||||
auto i = m_ToolOutput.indexOf("=");
|
auto i = m_toolStdout.indexOf("=");
|
||||||
if (i != -1) {
|
if (i != -1) {
|
||||||
i++;
|
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);
|
TlsFingerprint::local().trust(fingerprint, false);
|
||||||
emit info(tr("SSL fingerprint generated."));
|
qDebug("tls fingerprint generated");
|
||||||
|
return true;
|
||||||
} else {
|
} 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;
|
QStringList arguments;
|
||||||
arguments.append("rsa");
|
arguments.append("rsa");
|
||||||
|
|
@ -229,17 +256,19 @@ QString TlsCertificate::getCertKeyLength(const QString &path) {
|
||||||
arguments.append("-noout");
|
arguments.append("-noout");
|
||||||
|
|
||||||
if (!runTool(arguments)) {
|
if (!runTool(arguments)) {
|
||||||
return QString();
|
qFatal("failed to get key length from certificate");
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
const QString searchStart("Private-Key: (");
|
const QString searchStart("Private-Key: (");
|
||||||
const QString searchEnd(" bit");
|
const QString searchEnd(" bit");
|
||||||
|
|
||||||
// Get the line that contains the key length from the output
|
// Get the line that contains the key length from the output
|
||||||
const auto indexStart = m_ToolOutput.indexOf(searchStart);
|
const auto indexStart = m_toolStdout.indexOf(searchStart);
|
||||||
const auto indexEnd = m_ToolOutput.indexOf(searchEnd, indexStart);
|
const auto indexEnd = m_toolStdout.indexOf(searchEnd, indexStart);
|
||||||
const auto start = indexStart + searchStart.length();
|
const auto start = indexStart + searchStart.length();
|
||||||
const auto end = indexEnd - (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 "TlsFingerprint.h"
|
||||||
|
|
||||||
#include "gui/CoreInterface.h"
|
#include "gui/core/CoreTool.h"
|
||||||
|
|
||||||
#include <QDir>
|
#include <QDir>
|
||||||
#include <QTextStream>
|
#include <QTextStream>
|
||||||
|
|
@ -27,11 +27,10 @@ static const char kLocalFilename[] = "Local.txt";
|
||||||
static const char kTrustedServersFilename[] = "TrustedServers.txt";
|
static const char kTrustedServersFilename[] = "TrustedServers.txt";
|
||||||
static const char kTrustedClientsFilename[] = "TrustedClients.txt";
|
static const char kTrustedClientsFilename[] = "TrustedClients.txt";
|
||||||
|
|
||||||
TlsFingerprint::TlsFingerprint(const QString &filename) {
|
TlsFingerprint::TlsFingerprint(const QString &filename)
|
||||||
m_Filename = filename;
|
: m_Filename(filename) {}
|
||||||
}
|
|
||||||
|
|
||||||
void TlsFingerprint::trust(const QString &fingerprintText, bool append) {
|
void TlsFingerprint::trust(const QString &fingerprintText, bool append) const {
|
||||||
TlsFingerprint::persistDirectory();
|
TlsFingerprint::persistDirectory();
|
||||||
|
|
||||||
QIODevice::OpenMode openMode;
|
QIODevice::OpenMode openMode;
|
||||||
|
|
@ -59,7 +58,7 @@ bool TlsFingerprint::fileExists() const {
|
||||||
return file.exists();
|
return file.exists();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool TlsFingerprint::isTrusted(const QString &fingerprintText) {
|
bool TlsFingerprint::isTrusted(const QString &fingerprintText) const {
|
||||||
QStringList list = readList();
|
QStringList list = readList();
|
||||||
foreach (QString trusted, list) {
|
foreach (QString trusted, list) {
|
||||||
if (trusted == fingerprintText) {
|
if (trusted == fingerprintText) {
|
||||||
|
|
@ -69,7 +68,7 @@ bool TlsFingerprint::isTrusted(const QString &fingerprintText) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
QStringList TlsFingerprint::readList(const int readTo) {
|
QStringList TlsFingerprint::readList(const int readTo) const {
|
||||||
QStringList list;
|
QStringList list;
|
||||||
|
|
||||||
QString dirName = TlsFingerprint::directoryPath();
|
QString dirName = TlsFingerprint::directoryPath();
|
||||||
|
|
@ -93,7 +92,7 @@ QStringList TlsFingerprint::readList(const int readTo) {
|
||||||
return list;
|
return list;
|
||||||
}
|
}
|
||||||
|
|
||||||
QString TlsFingerprint::readFirst() {
|
QString TlsFingerprint::readFirst() const {
|
||||||
QStringList list = readList(1);
|
QStringList list = readList(1);
|
||||||
return list.at(0);
|
return list.at(0);
|
||||||
}
|
}
|
||||||
|
|
@ -111,8 +110,8 @@ void TlsFingerprint::persistDirectory() {
|
||||||
}
|
}
|
||||||
|
|
||||||
QString TlsFingerprint::directoryPath() {
|
QString TlsFingerprint::directoryPath() {
|
||||||
CoreInterface coreInterface;
|
CoreTool coreTool;
|
||||||
QString profileDir = coreInterface.getProfileDir();
|
QString profileDir = coreTool.getProfileDir();
|
||||||
|
|
||||||
return QString("%1/%2").arg(profileDir).arg(kDirName);
|
return QString("%1/%2").arg(profileDir).arg(kDirName);
|
||||||
}
|
}
|
||||||
|
|
@ -21,15 +21,7 @@
|
||||||
|
|
||||||
class TlsFingerprint {
|
class TlsFingerprint {
|
||||||
private:
|
private:
|
||||||
TlsFingerprint(const QString &filename);
|
explicit 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;
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
static TlsFingerprint local();
|
static TlsFingerprint local();
|
||||||
|
|
@ -40,6 +32,13 @@ public:
|
||||||
static bool localFingerprintExists();
|
static bool localFingerprintExists();
|
||||||
static void persistDirectory();
|
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:
|
private:
|
||||||
QString m_Filename;
|
QString m_Filename;
|
||||||
};
|
};
|
||||||
|
|
@ -20,10 +20,17 @@
|
||||||
#include "TlsCertificate.h"
|
#include "TlsCertificate.h"
|
||||||
#include "constants.h"
|
#include "constants.h"
|
||||||
|
|
||||||
|
#include <QString>
|
||||||
|
|
||||||
namespace synergy::gui {
|
namespace synergy::gui {
|
||||||
|
|
||||||
|
TlsUtility::TlsUtility(
|
||||||
|
const IAppConfig &appConfig, const license::License &license)
|
||||||
|
: m_appConfig(appConfig),
|
||||||
|
m_license(license) {}
|
||||||
|
|
||||||
bool TlsUtility::isAvailable() const {
|
bool TlsUtility::isAvailable() const {
|
||||||
return !kLicensingEnabled || m_license.isTlsAvailable();
|
return !kEnableActivation || m_license.isTlsAvailable();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool TlsUtility::isAvailableAndEnabled() const {
|
bool TlsUtility::isAvailableAndEnabled() const {
|
||||||
|
|
@ -31,20 +38,20 @@ bool TlsUtility::isAvailableAndEnabled() const {
|
||||||
return isAvailable() && config.tlsEnabled();
|
return isAvailable() && config.tlsEnabled();
|
||||||
}
|
}
|
||||||
|
|
||||||
void TlsUtility::generateCertificate(bool replace) const {
|
bool TlsUtility::generateCertificate() {
|
||||||
qDebug("generating tls certificate, "
|
qDebug("generating tls certificate, "
|
||||||
"all clients must trust the new fingerprint");
|
"all clients must trust the new fingerprint");
|
||||||
|
|
||||||
if (!isAvailableAndEnabled()) {
|
if (!isAvailableAndEnabled()) {
|
||||||
qFatal("unable to generate tls certificate, "
|
qCritical("unable to generate tls certificate, "
|
||||||
"tls is either not available or not enabled");
|
"tls is either not available or not enabled");
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto path = m_appConfig.tlsCertPath();
|
auto path = m_appConfig.tlsCertPath();
|
||||||
auto length = m_appConfig.tlsKeyLength();
|
auto length = m_appConfig.tlsKeyLength();
|
||||||
|
|
||||||
TlsCertificate cert;
|
return m_certificate.generateCertificate(path, length);
|
||||||
cert.generateCertificate(path, length, replace);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace synergy::gui
|
} // namespace synergy::gui
|
||||||
|
|
@ -17,18 +17,23 @@
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "IAppConfig.h"
|
#include "gui/config/IAppConfig.h"
|
||||||
|
|
||||||
|
#include "TlsCertificate.h"
|
||||||
#include "license/License.h"
|
#include "license/License.h"
|
||||||
|
|
||||||
|
#include <QObject>
|
||||||
|
|
||||||
namespace synergy::gui {
|
namespace synergy::gui {
|
||||||
|
|
||||||
class TlsUtility {
|
class TlsUtility : public QObject {
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
public:
|
public:
|
||||||
explicit TlsUtility(
|
explicit TlsUtility(
|
||||||
const IAppConfig &appConfig, const license::License &license)
|
const IAppConfig &appConfig, const license::License &license);
|
||||||
: m_appConfig(appConfig),
|
|
||||||
m_license(license) {}
|
bool generateCertificate();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Combines the availability and the enabled status of TLS.
|
* @brief Combines the availability and the enabled status of TLS.
|
||||||
|
|
@ -44,14 +49,10 @@ public:
|
||||||
*/
|
*/
|
||||||
bool isAvailable() const;
|
bool isAvailable() const;
|
||||||
|
|
||||||
/**
|
|
||||||
* @param replace Replace certificate on disk (default: false).
|
|
||||||
*/
|
|
||||||
void generateCertificate(bool replace = false) const;
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
const IAppConfig &m_appConfig;
|
const IAppConfig &m_appConfig;
|
||||||
const license::License &m_license;
|
const license::License &m_license;
|
||||||
|
TlsCertificate m_certificate;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace synergy::gui
|
} // namespace synergy::gui
|
||||||
|
|
@ -56,7 +56,11 @@ Screen::~Screen() {
|
||||||
disable();
|
disable();
|
||||||
}
|
}
|
||||||
assert(!m_enabled);
|
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);
|
assert(m_entered == m_isPrimary);
|
||||||
|
|
||||||
delete m_screen;
|
delete m_screen;
|
||||||
LOG((CLOG_DEBUG "closed display"));
|
LOG((CLOG_DEBUG "closed display"));
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -15,14 +15,23 @@
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "gui/CommandProcess.h"
|
#include "gui/core/CommandProcess.h"
|
||||||
|
|
||||||
#include <gtest/gtest.h>
|
#include <gtest/gtest.h>
|
||||||
|
|
||||||
TEST(CommandProcessTests, run_commandSucceeds_returnsOutput) {
|
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 QString command = "echo";
|
||||||
const QStringList arguments = {"Hello, World!"};
|
const QStringList arguments = {"Hello, World!"};
|
||||||
const QString input = "";
|
const QString input = "";
|
||||||
|
#endif // Q_OS_WIN
|
||||||
|
|
||||||
CommandProcess commandProcess(command, arguments, input);
|
CommandProcess commandProcess(command, arguments, input);
|
||||||
|
|
||||||
|
|
@ -15,41 +15,41 @@
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "gui/CoreInterface.h"
|
#include "gui/core/CoreTool.h"
|
||||||
#include "shared/gui/QtCoreTest.h"
|
#include "shared/gui/QtCoreTest.h"
|
||||||
|
|
||||||
#include <gtest/gtest.h>
|
#include <gtest/gtest.h>
|
||||||
|
|
||||||
class CoreInterfaceTests : public QtCoreTest {};
|
class CoreToolTests : public QtCoreTest {};
|
||||||
|
|
||||||
TEST_F(CoreInterfaceTests, getProfileDir_noMock_returnsNonEmpty) {
|
TEST_F(CoreToolTests, getProfileDir_noMock_returnsNonEmpty) {
|
||||||
CoreInterface coreInterface;
|
CoreTool coreTool;
|
||||||
|
|
||||||
QString profileDir = coreInterface.getProfileDir();
|
QString profileDir = coreTool.getProfileDir();
|
||||||
|
|
||||||
EXPECT_FALSE(profileDir.isEmpty());
|
EXPECT_FALSE(profileDir.isEmpty());
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(CoreInterfaceTests, getInstalledDir_noMock_returnsNonEmpty) {
|
TEST_F(CoreToolTests, getInstalledDir_noMock_returnsNonEmpty) {
|
||||||
CoreInterface coreInterface;
|
CoreTool coreTool;
|
||||||
|
|
||||||
QString installedDir = coreInterface.getInstalledDir();
|
QString installedDir = coreTool.getInstalledDir();
|
||||||
|
|
||||||
EXPECT_FALSE(installedDir.isEmpty());
|
EXPECT_FALSE(installedDir.isEmpty());
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(CoreInterfaceTests, getArch_noMock_returnsNonEmpty) {
|
TEST_F(CoreToolTests, getArch_noMock_returnsNonEmpty) {
|
||||||
CoreInterface coreInterface;
|
CoreTool coreTool;
|
||||||
|
|
||||||
QString arch = coreInterface.getArch();
|
QString arch = coreTool.getArch();
|
||||||
|
|
||||||
EXPECT_FALSE(arch.isEmpty());
|
EXPECT_FALSE(arch.isEmpty());
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(CoreInterfaceTests, getSerialKeyFilePath_noMock_returnsNonEmpty) {
|
TEST_F(CoreToolTests, getSerialKeyFilePath_noMock_returnsNonEmpty) {
|
||||||
CoreInterface coreInterface;
|
CoreTool coreTool;
|
||||||
|
|
||||||
QString serialKeyFilePath = coreInterface.getSerialKeyFilePath();
|
QString serialKeyFilePath = coreTool.getSerialKeyFilePath();
|
||||||
|
|
||||||
EXPECT_FALSE(serialKeyFilePath.isEmpty());
|
EXPECT_FALSE(serialKeyFilePath.isEmpty());
|
||||||
}
|
}
|
||||||
|
|
@ -33,7 +33,7 @@ void TestEventQueue::initQuitTimeout(double timeout) {
|
||||||
|
|
||||||
void TestEventQueue::cleanupQuitTimeout() {
|
void TestEventQueue::cleanupQuitTimeout() {
|
||||||
removeHandler(Event::kTimer, m_pQuitTimeoutTimer);
|
removeHandler(Event::kTimer, m_pQuitTimeoutTimer);
|
||||||
delete m_pQuitTimeoutTimer;
|
deleteTimer(m_pQuitTimeoutTimer);
|
||||||
m_pQuitTimeoutTimer = nullptr;
|
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/>.
|
* 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"
|
||||||
#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/>.
|
* 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 <gmock/gmock.h>
|
||||||
#include <gtest/gtest.h>
|
#include <gtest/gtest.h>
|
||||||
|
|
@ -25,7 +25,7 @@ using testing::StrEq;
|
||||||
|
|
||||||
class MockStream : public QDataStreamProxy {
|
class MockStream : public QDataStreamProxy {
|
||||||
public:
|
public:
|
||||||
MOCK_METHOD(qint64, writeRawData, (const char *, qint64), (override));
|
MOCK_METHOD(qint64, writeRawData, (const char *, int), (override));
|
||||||
};
|
};
|
||||||
|
|
||||||
TEST(QIpcClientTests, sendCommand_anyCommand_commandSent) {
|
TEST(QIpcClientTests, sendCommand_anyCommand_commandSent) {
|
||||||
|
|
@ -38,5 +38,5 @@ TEST(QIpcClientTests, sendCommand_anyCommand_commandSent) {
|
||||||
EXPECT_CALL(*mockStream, writeRawData(StrEq("test"), 4)).Times(1);
|
EXPECT_CALL(*mockStream, writeRawData(StrEq("test"), 4)).Times(1);
|
||||||
|
|
||||||
QIpcClient ipcClient(streamProvider);
|
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/>.
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "gui/LicenseHandler.h"
|
#include "gui/license/LicenseHandler.h"
|
||||||
|
|
||||||
#include <chrono>
|
#include <chrono>
|
||||||
#include <gtest/gtest.h>
|
#include <gtest/gtest.h>
|
||||||
|
|
@ -15,7 +15,7 @@
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "gui/license_notices.h"
|
#include "gui/license/license_notices.h"
|
||||||
|
|
||||||
#include <chrono>
|
#include <chrono>
|
||||||
#include <gmock/gmock.h>
|
#include <gmock/gmock.h>
|
||||||
Loading…
Reference in a new issue