Implement safer memory use, improve dev env, fixed GUI bugs (#7407)

* Improve dev script for daemon

* Ignore `.user` files created by Qt

* Add `FORCE_DESKTOP_PROCESS` option

* Catch errors related to getting profile dir

* Disable IPC entirely if forced desktop

* Use in-class init for AppConfig members

* Refactor config to use safer memory management

* Improve launch config to make OS-specific debugger usage clearer

* Re-enable MainWindowTests on Windows, further refactor for memory safety, fixed some include orders

* Remove dead singleton code

* Swap order of tests

* Use HTTPs for URLs

* Fixed compile errors for `SYNERGY_ENABLE_LICENSING` compile path

* Restore exec function call

* Remove extra link in cancel dialog

* Fixed broken link on activation cancel UI

* Close dialog if activated

* Fixed macOS enum ref to kCurrentProcess

* Improve wording on cancel activation dialog

* WIP - Test timeout (compile error)

* Finished timeout logic (with smart pointer)

* Include string_view

* Switch to thread from jthread (maybe not supported by macOS compiler?)

* Improve comment

* Disable test on Windows

* Add TODO related to jthread on macOS

* Refactor settings and paths on Windows

* Launch in desktop mode on Windows

* Remove arg quote wraps which break desktop mode

* Fixed qFatal on Linux

* Remove test value

* Follow original `AppConfig` accessor convention

* Disable service checkbox if not Windows

* Simplify TLS control enable logic

* Update command and Git ignore

* Fixed code style

* Fixed include consistency

* Fixed includes in validator

* Fixed lint errors

* Update ChangeLog

* Use smart pointer for core process

* Remove unneccesary default operators

* Don't halt on stderr
This commit is contained in:
Nick Bolton 2024-07-22 17:48:02 +01:00 committed by GitHub
parent f81901f633
commit 85b8b83a53
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
58 changed files with 2244 additions and 2221 deletions

2
.gitignore vendored
View file

@ -14,3 +14,5 @@ Brewfile.lock.json
*.code-workspace *.code-workspace
.env* .env*
/scripts/*.egg-info /scripts/*.egg-info
/*.user
*.ui.autosave

71
.vscode/launch.json vendored
View file

@ -2,55 +2,68 @@
"version": "0.2.0", "version": "0.2.0",
"configurations": [ "configurations": [
{ {
"name": "gui lldb", "name": "gui unix",
"type": "lldb", "type": "lldb",
"request": "launch", "request": "launch",
"program": "${workspaceFolder}/build/bin/synergy", "program": "${workspaceFolder}/build/bin/synergy",
"preLaunchTask": "build-kill" "preLaunchTask": "kill-build"
}, },
{ {
"name": "unittests lldb", "name": "gui windows",
"type": "cppvsdbg",
"cwd": "${workspaceRoot}/build/bin",
"request": "launch",
"program": "${workspaceFolder}/build/bin/synergy",
"internalConsoleOptions": "openOnSessionStart",
"preLaunchTask": "kill-build"
},
{
"name": "unittests unix",
"type": "lldb", "type": "lldb",
"request": "launch", "request": "launch",
"program": "${workspaceFolder}/build/bin/unittests", "program": "${workspaceFolder}/build/bin/unittests",
"preLaunchTask": "build" "preLaunchTask": "build"
}, },
{ {
"name": "integtests lldb", "name": "unittests windows",
"type": "cppvsdbg",
"cwd": "${workspaceRoot}/build/bin",
"request": "launch",
"program": "${workspaceFolder}/build/bin/unittests",
"internalConsoleOptions": "openOnSessionStart",
"preLaunchTask": "build"
},
{
"name": "integtests unix",
"type": "lldb", "type": "lldb",
"request": "launch", "request": "launch",
"program": "${workspaceFolder}/build/bin/integtests", "program": "${workspaceFolder}/build/bin/integtests",
"preLaunchTask": "build" "preLaunchTask": "build"
}, },
{ {
"name": "win daemon attach", "name": "integtests windows",
"type": "cppvsdbg",
"cwd": "${workspaceRoot}/build/bin",
"request": "launch",
"program": "${workspaceFolder}/build/bin/integtests",
"internalConsoleOptions": "openOnSessionStart",
"preLaunchTask": "build"
},
{
"name": "daemon windows",
"type": "cppvsdbg",
"cwd": "${workspaceRoot}/build/bin",
"request": "launch",
"program": "${workspaceFolder}/build/bin/synergyd",
"args": ["-f"],
"internalConsoleOptions": "openOnSessionStart",
"preLaunchTask": "build"
},
{
"name": "daemon windows attach",
"type": "cppvsdbg", "type": "cppvsdbg",
"request": "attach", "request": "attach",
"processId": "${command:pickProcess}" "processId": "${command:pickProcess}"
},
{
"name": "win daemon attach - lldb",
"type": "lldb",
"request": "attach",
"program": "${workspaceFolder}/build/bin/synergyd"
},
{
"name": "win daemon launch",
"type": "cppvsdbg",
"cwd": "${workspaceRoot}/build/bin",
"request": "launch",
"program": "${workspaceFolder}/build/bin/synergyd",
"args": ["-f"],
"internalConsoleOptions": "openOnSessionStart"
},
{
"name": "win daemon launch - rebuild",
"type": "cppvsdbg",
"cwd": "${workspaceRoot}/build/bin",
"request": "launch",
"program": "${workspaceFolder}/build/bin/synergyd",
"args": ["-f"],
"preLaunchTask": "build"
} }
] ]
} }

31
.vscode/tasks.json vendored
View file

@ -22,7 +22,7 @@
}, },
{ {
"label": "tests", "label": "tests",
"dependsOn": ["unittests", "integtests"], "dependsOn": ["integtests", "unittests"],
"problemMatcher": [] "problemMatcher": []
}, },
{ {
@ -31,24 +31,43 @@
"command": "killall synergy; killall synergyc; killall synergys || true", "command": "killall synergy; killall synergyc; killall synergys || true",
"windows": { "windows": {
"command": "taskkill /F /IM synergy.exe /IM synergyc.exe /IM synergys.exe; $true" "command": "taskkill /F /IM synergy.exe /IM synergyc.exe /IM synergys.exe; $true"
},
"presentation": {
"reveal": "silent"
} }
}, },
{ {
"label": "build-kill", "label": "kill-build",
"dependsOn": ["build", "kill"] "dependsOn": ["kill", "build"],
"problemMatcher": []
}, },
{ {
"label": "gui", "label": "gui",
"type": "process", "type": "process",
"command": "${workspaceFolder}/build/bin/synergy", "command": "${workspaceFolder}/build/bin/synergy",
"dependsOn": ["build", "kill"] "dependsOn": ["build", "kill"],
"problemMatcher": [],
"windows": {
"command": "${workspaceFolder}/build/bin/synergy.exe"
}
}, },
{ {
"label": "windows daemon", "label": "restart daemon",
"type": "shell", "type": "shell",
"command": "python scripts/windows_daemon.py", "command": "python scripts/daemon.py --restart",
"dependsOn": ["build"] "dependsOn": ["build"]
}, },
{
"label": "reinstall daemon",
"type": "shell",
"command": "python scripts/daemon.py --reinstall",
"dependsOn": ["build"]
},
{
"label": "stop daemon",
"type": "shell",
"command": "python scripts/daemon.py --stop"
},
{ {
"label": "clean-gcda", "label": "clean-gcda",
"type": "shell", "type": "shell",

View file

@ -57,6 +57,7 @@ Enhancements:
- #7403 Solve low hanging reliability and maintainability issues - #7403 Solve low hanging reliability and maintainability issues
- #7404 Restore integtests and add to CI as warning comment on failure - #7404 Restore integtests and add to CI as warning comment on failure
- #7406 Migrate scripts from `requirements.txt` to `pyproject.toml` - #7406 Migrate scripts from `requirements.txt` to `pyproject.toml`
- #7407 Implement safer memory use, improve dev env, fixed GUI bugs
# 1.14.6 # 1.14.6

View file

@ -1,19 +1,3 @@
# Synergy -- mouse and keyboard sharing utility
# Copyright (C) 2012-2024 Symless Ltd.
# Copyright (C) 2009-2012 Nick Bolton
#
# 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/>.
macro(configure_libs) macro(configure_libs)
set(libs) set(libs)
@ -295,6 +279,9 @@ macro(configure_windows_libs)
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/res/win/version.rc.in configure_file(${CMAKE_CURRENT_SOURCE_DIR}/res/win/version.rc.in
${CMAKE_BINARY_DIR}/src/version.rc @ONLY) ${CMAKE_BINARY_DIR}/src/version.rc @ONLY)
find_openssl_dir_win32(OPENSSL_PATH)
add_definitions(-DOPENSSL_PATH="${OPENSSL_PATH}")
endmacro() endmacro()
macro(configure_openssl) macro(configure_openssl)
@ -383,3 +370,36 @@ macro(configure_python)
set(PYTHON_BIN "${CMAKE_BINARY_DIR}/python/bin/python") set(PYTHON_BIN "${CMAKE_BINARY_DIR}/python/bin/python")
endif() endif()
endmacro() endmacro()
#
# Find the OpenSSL directory on Windows based on the location of the first
# `openssl` binary found.
#
function(find_openssl_dir_win32 result)
execute_process(
COMMAND where openssl
OUTPUT_VARIABLE OPENSSL_PATH
OUTPUT_STRIP_TRAILING_WHITESPACE)
# It's possible that there are multiple OpenSSL installations on the system,
# which is the case on GitHub runners. For now we'll pick the first one, but
# that's probably not very robust. Maybe our choco config could install to a
# specific location?
string(REGEX REPLACE "\r?\n" ";" OPENSSL_PATH_LIST ${OPENSSL_PATH})
message(STATUS "Found OpenSSL binaries at: ${OPENSSL_PATH_LIST}")
list(GET OPENSSL_PATH_LIST 0 OPENSSL_FIRST_PATH)
message(STATUS "First OpenSSL binary: ${OPENSSL_FIRST_PATH}")
get_filename_component(OPENSSL_BIN_DIR ${OPENSSL_FIRST_PATH} DIRECTORY)
message(STATUS "OpenSSL bin dir: ${OPENSSL_BIN_DIR}")
get_filename_component(OPENSSL_DIR ${OPENSSL_BIN_DIR} DIRECTORY)
message(STATUS "OpenSSL install root dir: ${OPENSSL_DIR}")
set(${result}
${OPENSSL_DIR}
PARENT_SCOPE)
endfunction()

View file

@ -51,8 +51,6 @@ macro(configure_windows_packaging)
set(CPACK_PACKAGE_VERSION ${SYNERGY_VERSION_MS}) set(CPACK_PACKAGE_VERSION ${SYNERGY_VERSION_MS})
set(QT_PATH $ENV{CMAKE_PREFIX_PATH}) set(QT_PATH $ENV{CMAKE_PREFIX_PATH})
find_openssl_dir_win32(OPENSSL_PATH)
configure_files(${CMAKE_CURRENT_SOURCE_DIR}/res/dist/wix configure_files(${CMAKE_CURRENT_SOURCE_DIR}/res/dist/wix
${CMAKE_BINARY_DIR}/installer) ${CMAKE_BINARY_DIR}/installer)
@ -157,36 +155,3 @@ macro(configure_files srcDir destDir)
endforeach(templateFile) endforeach(templateFile)
endmacro(configure_files) endmacro(configure_files)
#
# Find the OpenSSL directory on Windows based on the location of the first
# `openssl` binary found.
#
function(find_openssl_dir_win32 result)
execute_process(
COMMAND where openssl
OUTPUT_VARIABLE OPENSSL_PATH
OUTPUT_STRIP_TRAILING_WHITESPACE)
# It's possible that there are multiple OpenSSL installations on the system,
# which is the case on GitHub runners. For now we'll pick the first one, but
# that's probably not very robust. Maybe our choco config could install to a
# specific location?
string(REGEX REPLACE "\r?\n" ";" OPENSSL_PATH_LIST ${OPENSSL_PATH})
message(STATUS "Found OpenSSL binaries at: ${OPENSSL_PATH_LIST}")
list(GET OPENSSL_PATH_LIST 0 OPENSSL_FIRST_PATH)
message(STATUS "First OpenSSL binary: ${OPENSSL_FIRST_PATH}")
get_filename_component(OPENSSL_BIN_DIR ${OPENSSL_FIRST_PATH} DIRECTORY)
message(STATUS "OpenSSL bin dir: ${OPENSSL_BIN_DIR}")
get_filename_component(OPENSSL_DIR ${OPENSSL_BIN_DIR} DIRECTORY)
message(STATUS "OpenSSL install root dir: ${OPENSSL_DIR}")
set(${result}
${OPENSSL_DIR}
PARENT_SCOPE)
endfunction()

192
scripts/daemon.py Normal file
View file

@ -0,0 +1,192 @@
import os, sys, time, subprocess, argparse
import lib.windows as windows
import lib.file_utils as file_utils
import lib.env as env
import lib.colors as colors
import psutil
DEFAULT_BIN_NAME = "synergyd"
DEFAULT_SOURCE_DIR = os.path.join("build", "temp", "bin")
DEFAULT_TARGET_DIR = os.path.join("build", "bin")
SERVICE_NOT_RUNNING_ERROR = 2
ERROR_ACCESS_VIOLATION = 0xC0000005
IGNORE_PROCESSES = ["synergy.exe"]
class Context:
def __init__(self, verbose):
self.verbose = verbose
def main():
"""Entry point for the script."""
parser = argparse.ArgumentParser()
parser.add_argument("--reinstall", action="store_true")
parser.add_argument("--stop", action="store_true")
parser.add_argument("--restart", action="store_true")
parser.add_argument("--pause-on-exit", action="store_true")
parser.add_argument("--source-dir", default=DEFAULT_SOURCE_DIR)
parser.add_argument("--target-dir", default=DEFAULT_TARGET_DIR)
parser.add_argument("--bin-name", default=DEFAULT_BIN_NAME)
parser.add_argument("--verbose", action="store_true")
args = parser.parse_args()
if not env.is_windows():
print(
f"{colors.ERROR_TEXT} This script is only supported on Windows",
file=sys.stderr,
)
sys.exit(1)
context = Context(args.verbose)
try:
if args.reinstall:
reinstall(context, args.source_dir, args.target_dir, args.bin_name)
elif args.stop:
stop(context, args.target_dir)
elif args.restart:
restart(context, args.source_dir, args.target_dir)
else:
print("No action specified", file=sys.stderr)
exit(1)
except Exception as e:
print(f"{colors.ERROR_TEXT} {e}", file=sys.stderr)
if args.pause_on_exit:
input("Press enter to continue...")
def print_verbose(context, message):
if context.verbose:
print(message)
def ensure_admin():
if not windows.is_admin():
windows.relaunch_as_admin(__file__)
sys.exit()
def restart(context, source_dir, target_dir):
"""Stops the daemon service, copies files, and restarts the daemon service."""
ensure_admin()
stop(context, target_dir)
copy_files(source_dir, target_dir)
start()
def reinstall(context, source_dir, target_dir, bin_name):
"""Stops and uninstalls daemon service, copies files, and reinstalls the daemon service."""
ensure_admin()
stop(context, target_dir)
source_bin_path = f"{os.path.join(source_dir, bin_name)}.exe"
copy_files(source_dir, target_dir)
print("Removing old daemon service")
try:
subprocess.run([source_bin_path, "/uninstall"], shell=True, check=True)
except subprocess.CalledProcessError as e:
check_access_violation(e.returncode, source_bin_path)
if e.returncode != 0:
print(
f"{colors.WARNING_TEXT} Uninstall failed, return code: {e.returncode}",
file=sys.stderr,
)
target_bin_path = os.path.join(target_dir, bin_name + ".exe")
try:
print("Installing daemon service")
subprocess.run([target_bin_path, "/install"], shell=True, check=True)
except subprocess.CalledProcessError as e:
check_access_violation(e.returncode, target_bin_path)
if e.returncode != 0:
print(f"{colors.WARNING_TEXT} Install failed, return code: {e.returncode}")
def copy_files(source_dir, target_dir):
options = file_utils.CopyOptions(ignore_errors=True, verbose=False)
print(f"Copying files from {source_dir} to {target_dir}")
file_utils.copy(f"{source_dir}/*", target_dir, options)
def stop(context, target_dir):
ensure_admin()
print("Stopping daemon service")
try:
subprocess.run(["net", "stop", "synergy"], shell=True, check=True)
except subprocess.CalledProcessError as e:
if e.returncode == SERVICE_NOT_RUNNING_ERROR:
print_verbose(context, "Daemon service not running")
else:
raise e
# Wait for Windows to release the file handles after process termination.
wait_for_stop(context, target_dir)
def start():
ensure_admin()
print("Starting daemon service")
subprocess.run(["net", "start", "synergy"], shell=True, check=True)
def wait_for_stop(context, target_dir):
if is_any_process_running(context, target_dir):
print("Waiting for file handles to release...", end="", flush=True)
while is_any_process_running(context, target_dir):
if not context.verbose:
print(".", end="", flush=True)
time.sleep(1)
if not context.verbose:
print()
def check_access_violation(return_code, bin_path):
if return_code == ERROR_ACCESS_VIOLATION:
print(
f"{colors.WARNING_TEXT} Process crashed with memory access violation: {bin_path}",
file=sys.stderr,
)
def is_ignored_process(exe):
for ignore_process in IGNORE_PROCESSES:
if exe.endswith(ignore_process):
return True
return False
def is_any_process_running(context, dir):
"""Check if there is any running process that contains the given directory."""
print_verbose(context, f"Checking if any process is running in: {dir}")
for proc in psutil.process_iter(attrs=["name", "exe"]):
exe = proc.info["exe"]
if not exe:
print_verbose(context, f"Skipping process with no exe: {proc}")
continue
if is_ignored_process(exe):
print_verbose(context, f"Ignoring process: {exe}")
continue
try:
if dir.lower() in exe.lower():
print_verbose(context, f"Process found: {exe}")
return True
except (psutil.NoSuchProcess, psutil.AccessDenied):
pass
return False
main()

View file

@ -3,6 +3,7 @@
import argparse import argparse
import lib.env as env import lib.env as env
import lib.file_utils as file_utils import lib.file_utils as file_utils
import lib.colors as colors
def main(): def main():
@ -34,7 +35,7 @@ def main():
if not args.ignore_errors: if not args.ignore_errors:
raise e raise e
else: else:
print(f"Error: {e}") print(f"{colors.ERROR_TEXT} {e}")
if __name__ == "__main__": if __name__ == "__main__":

8
scripts/lib/colors.py Normal file
View file

@ -0,0 +1,8 @@
import colorama # type: ignore
from colorama import Fore # type: ignore
colorama.init()
ERROR_TEXT = f"{Fore.RED}Error:{Fore.RESET}"
WARNING_TEXT = f"{Fore.YELLOW}Warning:{Fore.RESET}"
HINT_TEXT = f"{Fore.LIGHTBLUE_EX}Hint:{Fore.RESET}"

View file

@ -1,9 +1,6 @@
import glob, os, shutil, sys import glob, os, shutil, sys
import lib.env as env
import colorama # type: ignore import lib.colors as colors
from colorama import Fore # type: ignore
colorama.init()
class CopyOptions: class CopyOptions:
@ -24,6 +21,8 @@ def copy(source, target, options):
context = CopyContext() context = CopyContext()
if options.verbose: if options.verbose:
print(f"Copying files from {source} to {target}") print(f"Copying files from {source} to {target}")
try:
for match in glob.glob(source): for match in glob.glob(source):
if os.path.isfile(match): if os.path.isfile(match):
@ -32,15 +31,13 @@ def copy(source, target, options):
copy_dir(match, target, options, context) copy_dir(match, target, options, context)
else: else:
raise RuntimeError(f"Path {match} is not a file or directory") raise RuntimeError(f"Path {match} is not a file or directory")
finally:
if context.errors and options.ignore_errors: if context.errors and options.ignore_errors:
print( print(f"{colors.WARNING_TEXT} Ignored {context.errors} copy error(s)")
f"{Fore.YELLOW}WARNING:{Fore.RESET} Ignored {context.errors} copy error(s)"
)
if context.permission_error: if context.permission_error and env.is_windows():
print( print(
f"{Fore.BLUE}HINT:{Fore.RESET} A permission error may mean that the file is in use" f"{colors.HINT_TEXT} A Windows file permission error may mean that the file is in use"
) )
@ -50,12 +47,8 @@ def copy_dir(match, target, options, context):
try: try:
shutil.copytree(match, target, dirs_exist_ok=True) shutil.copytree(match, target, dirs_exist_ok=True)
except PermissionError as e:
context.permission_error = True
handle_copy_error(e, options, context)
except Exception as e: except Exception as e:
handle_copy_error(e, options, context) handle_all_copy_errors(e, options, context)
def copy_file(match, target, options, context): def copy_file(match, target, options, context):
@ -64,17 +57,33 @@ def copy_file(match, target, options, context):
try: try:
shutil.copy(match, target) shutil.copy(match, target)
except PermissionError as e:
context.permission_error = True
handle_copy_error(e, options, context)
except Exception as e: except Exception as e:
handle_copy_error(e, options, context) handle_all_copy_errors(e, options, context)
def handle_copy_error(e, options, context): def handle_all_copy_errors(error, options, context):
context.errors += 1
if isinstance(error, shutil.Error):
for _, _, file_error in error.args[0]:
handle_copy_error(file_error, options, context)
else:
handle_copy_error(error, options, context)
if not options.ignore_errors: if not options.ignore_errors:
raise e raise error
else:
print(f"{Fore.YELLOW}WARNING:{Fore.RESET} Copy failed: {e}", file=sys.stderr)
def handle_copy_error(error, options, context):
if isinstance(error, PermissionError):
context.permission_error = True
if isinstance(error, str):
context.permission_error = error.startswith("[Errno 13] Permission denied")
context.errors += 1
if options.ignore_errors:
print(
f"{colors.WARNING_TEXT} Copy failed: {error}",
file=sys.stderr,
)

View file

@ -1,112 +0,0 @@
import os, sys, time, subprocess, argparse, glob
import lib.windows as windows
import lib.file_utils as file_utils
import psutil
DEFAULT_BIN_NAME = "synergyd"
DEFAULT_SOURCE_DIR = os.path.join("build", "temp", "bin")
DEFAULT_TARGET_DIR = os.path.join("build", "bin")
SERVICE_NOT_RUNNING_ERROR = 2
ERROR_ACCESS_VIOLATION = 0xC0000005
def main():
"""Entry point for the script."""
parser = argparse.ArgumentParser()
parser.add_argument("--pause-on-exit", action="store_true")
parser.add_argument("--source-dir", default=DEFAULT_SOURCE_DIR)
parser.add_argument("--target-dir", default=DEFAULT_TARGET_DIR)
parser.add_argument("--bin-name", default=DEFAULT_BIN_NAME)
args = parser.parse_args()
if not windows.is_admin():
windows.relaunch_as_admin(__file__)
sys.exit()
try:
reinstall(
args.source_dir,
args.target_dir,
args.bin_name,
)
except Exception as e:
print(f"Error: {e}", file=sys.stderr)
if args.pause_on_exit:
input("Press enter to continue...")
def reinstall(source_dir, target_dir, bin_name):
"""Stops the running daemon service, copies files, and reinstalls."""
print("Stopping daemon service")
try:
subprocess.run(["net", "stop", "synergy"], shell=True, check=True)
except subprocess.CalledProcessError as e:
if e.returncode == SERVICE_NOT_RUNNING_ERROR:
print("Daemon service not running")
else:
raise e
source_bin_path = f"{os.path.join(source_dir, bin_name)}.exe"
# Wait for Windows to release the file handles after process termination.
while is_any_process_running(target_dir):
print("Waiting for file handles to release")
time.sleep(1)
options = file_utils.CopyOptions(ignore_errors=True, verbose=False)
print(f"Copying files from {source_dir} to {target_dir}")
file_utils.copy(f"{source_dir}/*", target_dir, options)
print("Removing old daemon service")
try:
subprocess.run([source_bin_path, "/uninstall"], shell=True, check=True)
except subprocess.CalledProcessError as e:
check_access_violation(e.returncode, source_bin_path)
if e.returncode != 0:
print(
f"Warning: Uninstall failed, return code: {e.returncode}",
file=sys.stderr,
)
target_bin_path = os.path.join(target_dir, bin_name + ".exe")
try:
print("Installing daemon service")
subprocess.run([target_bin_path, "/install"], shell=True, check=True)
except subprocess.CalledProcessError as e:
check_access_violation(e.returncode, target_bin_path)
if e.returncode != 0:
print(f"Warning: Install failed, return code: {e.returncode}")
def check_access_violation(return_code, bin_path):
if return_code == ERROR_ACCESS_VIOLATION:
print(
f"Warning: Process crashed with memory access violation: {bin_path}",
file=sys.stderr,
)
def is_any_process_running(dir):
"""Check if there is any running process that contains the given directory."""
print(f"Checking if any process is running in: {dir}")
for proc in psutil.process_iter(attrs=["name", "exe"]):
exe = proc.info["exe"]
if not exe:
continue
try:
if dir.lower() in exe.lower():
print(f"Process found: {exe}")
return True
except (psutil.NoSuchProcess, psutil.AccessDenied):
pass
return False
main()

View file

@ -24,7 +24,7 @@ AboutDialog::AboutDialog(MainWindow *parent, const AppConfig &config)
Ui::AboutDialogBase() { Ui::AboutDialogBase() {
setupUi(this); setupUi(this);
m_versionChecker.setApp(parent->appPath(config.synergycName())); m_versionChecker.setApp(parent->appPath(config.coreClientName()));
QString version = SYNERGY_VERSION; QString version = SYNERGY_VERSION;
#ifdef GIT_SHA_SHORT #ifdef GIT_SHA_SHORT

View file

@ -1,17 +1,31 @@
/*
* synergy -- mouse and keyboard sharing utility
* Copyright (C) 2016 Synergy 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 "ActivationDialog.h" #include "ActivationDialog.h"
#include "ActivationNotifier.h"
#include "AppConfig.h" #include "AppConfig.h"
#include "CancelActivationDialog.h" #include "CancelActivationDialog.h"
#include "FailedLoginDialog.h"
#include "LicenseManager.h" #include "LicenseManager.h"
#include "MainWindow.h" #include "MainWindow.h"
#include "QUtility.h" #include "shared/EditionType.h"
#include "ui_ActivationDialog.h" #include "ui_ActivationDialog.h"
#include <shared/EditionType.h>
#include <QApplication>
#include <QMessageBox> #include <QMessageBox>
#include <QThread> #include <QThread>
#include <iostream>
ActivationDialog::ActivationDialog( ActivationDialog::ActivationDialog(
QWidget *parent, AppConfig &appConfig, LicenseManager &licenseManager) QWidget *parent, AppConfig &appConfig, LicenseManager &licenseManager)
@ -38,15 +52,18 @@ void ActivationDialog::refreshSerialKey() {
ActivationDialog::~ActivationDialog() { delete ui; } ActivationDialog::~ActivationDialog() { delete ui; }
void ActivationDialog::reject() { void ActivationDialog::reject() {
if (m_LicenseManager->activeEdition() == kUnregistered) { // don't show the cancel confirmation dialog if they've already registered,
CancelActivationDialog cancelActivationDialog(this); // since it's not revent to customers who are changing their serial key.
if (QDialog::Accepted == cancelActivationDialog.exec()) { if (m_LicenseManager->activeEdition() != kUnregistered) {
m_LicenseManager->skipActivation(); QDialog::reject();
} else {
return; return;
} }
// the user is told that the 'No' button will exit the app.
CancelActivationDialog cancelActivationDialog(this);
if (cancelActivationDialog.exec() == QDialog::Rejected) {
QApplication::exit();
} }
QDialog::reject();
} }
void ActivationDialog::accept() { void ActivationDialog::accept() {
@ -82,7 +99,7 @@ void ActivationDialog::accept() {
.arg((daysLeft == 1) ? "" : "s") .arg((daysLeft == 1) ? "" : "s")
.arg((daysLeft == 1) ? "s" : ""); .arg((daysLeft == 1) ? "s" : "");
if (m_appConfig->isCryptoAvailable()) { if (m_appConfig->cryptoAvailable()) {
m_appConfig->generateCertificate(); m_appConfig->generateCertificate();
thanksMessage = thanksMessage =
thanksMessage.arg("If you're using SSL, " thanksMessage.arg("If you're using SSL, "

View file

@ -1,5 +1,21 @@
#ifndef ACTIVATIONDIALOG_H /*
#define ACTIVATIONDIALOG_H * synergy -- mouse and keyboard sharing utility
* Copyright (C) 2016 Synergy 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 <LicenseManager.h> #include <LicenseManager.h>
#include <QDialog> #include <QDialog>
@ -30,5 +46,3 @@ private:
AppConfig *m_appConfig; AppConfig *m_appConfig;
LicenseManager *m_LicenseManager; LicenseManager *m_LicenseManager;
}; };
#endif // ACTIVATIONDIALOG_H

View file

@ -6,8 +6,8 @@
<rect> <rect>
<x>0</x> <x>0</x>
<y>0</y> <y>0</y>
<width>410</width> <width>477</width>
<height>211</height> <height>241</height>
</rect> </rect>
</property> </property>
<property name="windowTitle"> <property name="windowTitle">
@ -18,7 +18,6 @@
<widget class="QLabel" name="label"> <widget class="QLabel" name="label">
<property name="font"> <property name="font">
<font> <font>
<weight>75</weight>
<bold>true</bold> <bold>true</bold>
</font> </font>
</property> </property>
@ -30,7 +29,7 @@
<item> <item>
<widget class="QLabel" name="label_5"> <widget class="QLabel" name="label_5">
<property name="text"> <property name="text">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;This can be found on your &lt;a href=&quot;https://symless.com/synergy/account?source=gui&quot;&gt;&lt;span style=&quot; text-decoration: none; color: #4285F4;&quot;&gt;account&lt;/span&gt;&lt;/a&gt; page.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string> <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Your serial key is on your &lt;a href=&quot;https://symless.com/synergy/account?source=gui&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#4285f4;&quot;&gt;account&lt;/span&gt;&lt;/a&gt; page. Don't have a license? &lt;a href=&quot;https://symless.com/synergy/purchase?source=gui&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#007af4;&quot;&gt;Purchase Synergy&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property> </property>
<property name="openExternalLinks"> <property name="openExternalLinks">
<bool>true</bool> <bool>true</bool>
@ -47,9 +46,12 @@
</property> </property>
<property name="html"> <property name="html">
<string>&lt;!DOCTYPE HTML PUBLIC &quot;-//W3C//DTD HTML 4.0//EN&quot; &quot;http://www.w3.org/TR/REC-html40/strict.dtd&quot;&gt; <string>&lt;!DOCTYPE HTML PUBLIC &quot;-//W3C//DTD HTML 4.0//EN&quot; &quot;http://www.w3.org/TR/REC-html40/strict.dtd&quot;&gt;
&lt;html&gt;&lt;head&gt;&lt;meta name=&quot;qrichtext&quot; content=&quot;1&quot; /&gt;&lt;style type=&quot;text/css&quot;&gt; &lt;html&gt;&lt;head&gt;&lt;meta name=&quot;qrichtext&quot; content=&quot;1&quot; /&gt;&lt;meta charset=&quot;utf-8&quot; /&gt;&lt;style type=&quot;text/css&quot;&gt;
p, li { white-space: pre-wrap; } p, li { white-space: pre-wrap; }
&lt;/style&gt;&lt;/head&gt;&lt;body style=&quot; font-family:'.AppleSystemUIFont'; font-size:13pt; font-weight:400; font-style:normal;&quot;&gt; hr { height: 1px; border-width: 0; }
li.unchecked::marker { content: &quot;\2610&quot;; }
li.checked::marker { content: &quot;\2612&quot;; }
&lt;/style&gt;&lt;/head&gt;&lt;body style=&quot; font-family:'Segoe UI'; font-size:9pt; font-weight:400; font-style:normal;&quot;&gt;
&lt;p style=&quot;-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-family:'Sans'; font-size:10pt;&quot;&gt;&lt;br /&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string> &lt;p style=&quot;-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-family:'Sans'; font-size:10pt;&quot;&gt;&lt;br /&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property> </property>
<property name="acceptRichText"> <property name="acceptRichText">

View file

@ -1,6 +1,6 @@
/* /*
* synergy -- mouse and keyboard sharing utility * synergy -- mouse and keyboard sharing utility
* Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2012 Symless Ltd.
* Copyright (C) 2008 Volker Lanz (vl@fidra.de) * 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
@ -18,32 +18,35 @@
#include "AppConfig.h" #include "AppConfig.h"
#include "ConfigWriter.h" #include "Config.h"
#include "SslCertificate.h" #include "SslCertificate.h"
#include <QApplication>
#include <QPushButton> #include <QPushButton>
#include <QtCore> #include <QtCore>
#include <QtNetwork> #include <QtNetwork>
#include <QtWidgets/QMessageBox> #include <QtWidgets/QMessageBox>
#include <qapplication.h>
using synergy::gui::Config;
// this should be incremented each time the wizard is changed,
// which will force it to re-run for existing installations.
const int kWizardVersion = 8;
using GUI::Config::ConfigWriter;
#if defined(Q_OS_WIN) #if defined(Q_OS_WIN)
const char AppConfig::m_SynergysName[] = "synergys.exe"; const char AppConfig::m_CoreServerName[] = "synergys.exe";
const char AppConfig::m_SynergycName[] = "synergyc.exe"; const char AppConfig::m_CoreClientName[] = "synergyc.exe";
const char AppConfig::m_SynergyLogDir[] = "log/"; const char AppConfig::m_LogDir[] = "log/";
const char AppConfig::m_SynergyConfigName[] = "synergy.sgc"; const char AppConfig::m_ConfigFilename[] = "synergy.sgc";
const ProcessMode kDefaultProcessMode = ProcessMode::kService;
#else #else
const char AppConfig::m_SynergysName[] = "synergys"; const char AppConfig::m_CoreServerName[] = "synergys";
const char AppConfig::m_SynergycName[] = "synergyc"; const char AppConfig::m_CoreClientName[] = "synergyc";
const char AppConfig::m_SynergyLogDir[] = "/var/log/"; const char AppConfig::m_LogDir[] = "/var/log/";
const char AppConfig::m_SynergyConfigName[] = "synergy.conf"; const char AppConfig::m_ConfigFilename[] = "synergy.conf";
const ProcessMode kDefaultProcessMode = ProcessMode::kDesktop;
#endif #endif
const ElevateMode kDefaultElevateMode = ElevateAsNeeded; const char *const AppConfig::m_SettingsName[] = {
const char *AppConfig::m_SynergySettingsName[] = {
"screenName", "screenName",
"port", "port",
"interface", "interface",
@ -80,55 +83,38 @@ const char *AppConfig::m_SynergySettingsName[] = {
"licenseNextCheck", "licenseNextCheck",
"initiateConnectionFromServer", "initiateConnectionFromServer",
"clientHostMode", "clientHostMode",
"serverClientMode"}; "serverClientMode",
"serviceEnabled",
"minimizeOnClose"};
static const char *logLevelNames[] = {"INFO", "DEBUG", "DEBUG1", "DEBUG2"}; static const char *logLevelNames[] = {"INFO", "DEBUG", "DEBUG1", "DEBUG2"};
AppConfig::AppConfig(bool globalLoad) AppConfig::AppConfig() {
: m_ScreenName(), m_Config.registerReceiever(this);
m_Port(24800), load();
m_Interface(),
m_LogLevel(0),
m_LogToFile(),
m_WizardLastRun(0),
m_ProcessMode(kDefaultProcessMode),
m_StartedBefore(),
m_ElevateMode(kDefaultElevateMode),
m_Edition(kUnregistered),
m_CryptoEnabled(false),
m_AutoHide(false),
m_LastExpiringWarningTime(0),
m_ActivationHasRun(),
m_MinimizeToTray(false),
m_ServerGroupChecked(),
m_UseExternalConfig(),
m_UseInternalConfig(),
m_ClientGroupChecked(),
m_LoadFromSystemScope() {
auto writer = ConfigWriter::make();
// Register this class to receive global load and saves
writer->registerClass(this);
// HACK: enable global load by default but allow it to be disabled for tests.
// when run in a test environment, this function causes a segfault.
if (globalLoad) {
writer->globalLoad();
} }
void AppConfig::load() {
m_Config.loadAll();
// User settings exist and the load from system scope variable is true // User settings exist and the load from system scope variable is true
if (writer->hasSetting( if (m_Config.hasSetting(
settingName(Setting::kLoadSystemSettings), ConfigWriter::kUser)) { settingName(Setting::kLoadSystemSettings), Config::Scope::User)) {
setLoadFromSystemScope(m_LoadFromSystemScope); setLoadFromSystemScope(m_LoadFromSystemScope);
} }
// If user setting don't exist but system ones do, load the system settings // If user setting don't exist but system ones do, load the system settings
else if (writer->hasSetting( else if (m_Config.hasSetting(
settingName(Setting::kScreenName), ConfigWriter::kSystem)) { settingName(Setting::kScreenName), Config::Scope::System)) {
setLoadFromSystemScope(true); setLoadFromSystemScope(true);
} }
} }
void AppConfig::applyAppSettings() const {
QApplication::setQuitOnLastWindowClosed(!m_MinimizeOnClose);
}
Config &AppConfig::config() { return m_Config; }
const QString &AppConfig::screenName() const { return m_ScreenName; } const QString &AppConfig::screenName() const { return m_ScreenName; }
int AppConfig::port() const { return m_Port; } int AppConfig::port() const { return m_Port; }
@ -141,18 +127,13 @@ 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::synergyLogDir() const { QString AppConfig::logDir() const {
// by default log to home dir // by default log to home dir
return QDir::home().absolutePath() + "/"; return QDir::home().absolutePath() + "/";
} }
QString AppConfig::synergyProgramDir() const { void AppConfig::persistLogDir() const {
// synergy binaries should be in the same dir. QDir dir = logDir();
return QCoreApplication::applicationDirPath() + "/";
}
void AppConfig::persistLogDir() {
QDir dir = synergyLogDir();
// persist the log directory // persist the log directory
if (!dir.exists()) { if (!dir.exists()) {
@ -160,18 +141,11 @@ void AppConfig::persistLogDir() {
} }
} }
const QString AppConfig::logFilenameCmd() const {
QString filename = m_LogFilename;
#if defined(Q_OS_WIN)
// wrap in quotes in case username contains spaces.
filename = QString("\"%1\"").arg(filename);
#endif
return filename;
}
QString AppConfig::logLevelText() const { return logLevelNames[logLevel()]; } QString AppConfig::logLevelText() const { return logLevelNames[logLevel()]; }
ProcessMode AppConfig::processMode() const { return m_ProcessMode; } ProcessMode AppConfig::processMode() const {
return m_ServiceEnabled ? ProcessMode::kService : ProcessMode::kDesktop;
}
bool AppConfig::wizardShouldRun() const { bool AppConfig::wizardShouldRun() const {
return m_WizardLastRun < kWizardVersion; return m_WizardLastRun < kWizardVersion;
@ -180,141 +154,137 @@ bool AppConfig::wizardShouldRun() const {
bool AppConfig::startedBefore() const { return m_StartedBefore; } bool AppConfig::startedBefore() const { return m_StartedBefore; }
void AppConfig::loadSettings() { void AppConfig::loadSettings() {
using enum AppConfig::Setting;
m_ScreenName = m_ScreenName =
loadSetting(Setting::kScreenName, QHostInfo::localHostName()).toString(); loadSetting(kScreenName, QHostInfo::localHostName()).toString();
if (m_ScreenName.isEmpty()) { if (m_ScreenName.isEmpty()) {
m_ScreenName = QHostInfo::localHostName(); m_ScreenName = QHostInfo::localHostName();
} }
m_Port = loadSetting(Setting::kPort, 24800).toInt(); m_Port = loadSetting(kPort, 24800).toInt();
m_Interface = loadSetting(Setting::kInterfaceSetting).toString(); m_Interface = loadSetting(kInterfaceSetting).toString();
m_LogLevel = loadSetting(Setting::kLogLevel, 0).toInt(); m_LogLevel = loadSetting(kLogLevel, 0).toInt();
m_LogToFile = loadSetting(Setting::kLogToFile, false).toBool(); m_LogToFile = loadSetting(kLogToFile, false).toBool();
m_LogFilename = m_LogFilename =
loadSetting(Setting::kLogFilename, synergyLogDir() + "synergy.log") loadSetting(kLogFilename, logDir() + "synergy.log").toString();
.toString(); m_WizardLastRun = loadCommonSetting(kWizardLastRun, 0).toInt();
m_WizardLastRun = loadCommonSetting(Setting::kWizardLastRun, 0).toInt(); m_StartedBefore = loadSetting(kStartedBefore, false).toBool();
m_StartedBefore = loadSetting(Setting::kStartedBefore, false).toBool();
{ // Scope related code together QVariant elevateMode = loadSetting(kElevateModeEnum);
// TODO Investigate why kElevateModeEnum isn't loaded fully
QVariant elevateMode = loadSetting(Setting::kElevateModeEnum);
if (!elevateMode.isValid()) { if (!elevateMode.isValid()) {
elevateMode = loadSetting( elevateMode = loadSetting(
Setting::kElevateModeSetting, kElevateModeSetting, QVariant(static_cast<int>(kDefaultElevateMode)));
QVariant(static_cast<int>(kDefaultElevateMode)));
} }
m_ElevateMode = static_cast<ElevateMode>(elevateMode.toInt()); m_ElevateMode = static_cast<ElevateMode>(elevateMode.toInt());
}
m_ActivateEmail = loadSetting(Setting::kActivateEmail, "").toString(); m_ActivateEmail = loadSetting(kActivateEmail, "").toString();
m_CryptoEnabled = loadSetting(Setting::kCryptoEnabled, true).toBool(); m_CryptoEnabled = loadSetting(kCryptoEnabled, true).toBool();
m_AutoHide = loadSetting(Setting::kAutoHide, false).toBool(); m_AutoHide = loadSetting(kAutoHide, false).toBool();
m_lastVersion = loadSetting(Setting::kLastVersion, "Unknown").toString(); m_LastVersion = loadSetting(kLastVersion, "Unknown").toString();
m_LastExpiringWarningTime = m_LastExpiringWarningTime = loadSetting(kLastExpireWarningTime, 0).toInt();
loadSetting(Setting::kLastExpireWarningTime, 0).toInt(); m_ActivationHasRun = loadSetting(kActivationHasRun, false).toBool();
m_ActivationHasRun = loadSetting(Setting::kActivationHasRun, false).toBool(); m_MinimizeToTray = loadSetting(kMinimizeToTray, false).toBool();
m_MinimizeToTray = loadSetting(Setting::kMinimizeToTray, false).toBool();
m_LoadFromSystemScope = m_LoadFromSystemScope =
loadCommonSetting(Setting::kLoadSystemSettings, false).toBool(); loadCommonSetting(kLoadSystemSettings, false).toBool();
m_ServerGroupChecked = m_ServerGroupChecked = loadSetting(kGroupServerCheck, false).toBool();
loadSetting(Setting::kGroupServerCheck, false).toBool(); m_UseExternalConfig = loadSetting(kUseExternalConfig, false).toBool();
m_UseExternalConfig =
loadSetting(Setting::kUseExternalConfig, false).toBool();
m_ConfigFile = m_ConfigFile =
loadSetting( loadSetting(kConfigFile, QDir::homePath() + "/" + m_ConfigFilename)
Setting::kConfigFile, QDir::homePath() + "/" + m_SynergyConfigName)
.toString(); .toString();
m_UseInternalConfig = m_UseInternalConfig = loadSetting(kUseInternalConfig, false).toBool();
loadSetting(Setting::kUseInternalConfig, false).toBool(); m_ClientGroupChecked = loadSetting(kGroupClientCheck, false).toBool();
m_ClientGroupChecked = m_ServerHostname = loadSetting(kServerHostname).toString();
loadSetting(Setting::kGroupClientCheck, false).toBool(); m_PreventSleep = loadSetting(kPreventSleep, false).toBool();
m_ServerHostname = loadSetting(Setting::kServerHostname).toString(); m_LanguageSync = loadSetting(kLanguageSync, false).toBool();
m_PreventSleep = loadSetting(Setting::kPreventSleep, false).toBool(); m_InvertScrollDirection = loadSetting(kInvertScrollDirection, false).toBool();
m_LanguageSync = loadSetting(Setting::kLanguageSync, false).toBool(); m_Guid = loadCommonSetting(kGuid, QUuid::createUuid()).toString();
m_InvertScrollDirection = m_licenseNextCheck = loadCommonSetting(kLicenseNextCheck, 0).toULongLong();
loadSetting(Setting::kInvertScrollDirection, false).toBool(); m_ClientHostMode = loadSetting(kClientHostMode, true).toBool();
m_guid = loadCommonSetting(Setting::kGuid, QUuid::createUuid()).toString(); m_ServerClientMode = loadSetting(kServerClientMode, true).toBool();
m_licenseRegistryUrl = loadCommonSetting(
Setting::kLicenseRegistryUrl,
"https://api2.prod.symless.com/license/register")
.toString();
m_licenseNextCheck =
loadCommonSetting(Setting::kLicenseNextCheck, 0).toULongLong();
m_ClientHostMode = loadSetting(Setting::kClientHostMode, true).toBool();
m_ServerClientMode = loadSetting(Setting::kServerClientMode, true).toBool();
m_InitiateConnectionFromServer = m_InitiateConnectionFromServer =
loadSetting(Setting::kInitiateConnectionFromServer, false).toBool(); loadSetting(kInitiateConnectionFromServer, false).toBool();
// only change the serial key if the settings being loaded contains a key // only change the serial key if the settings being loaded contains a key
bool updateSerial = ConfigWriter::make()->hasSetting( bool updateSerial = m_Config.hasSetting(
settingName(Setting::kLoadSystemSettings), ConfigWriter::kCurrent); settingName(kLoadSystemSettings), Config::Scope::Current);
// if the setting exists and is not empty // if the setting exists and is not empty
updateSerial = updateSerial = updateSerial &&
updateSerial && !loadSetting(kSerialKey, "").toString().trimmed().isEmpty();
!loadSetting(Setting::kSerialKey, "").toString().trimmed().isEmpty();
if (updateSerial) { if (updateSerial) {
m_Serialkey = loadSetting(Setting::kSerialKey, "").toString().trimmed(); m_Serialkey = loadSetting(kSerialKey, "").toString().trimmed();
m_Edition = static_cast<Edition>( m_Edition = static_cast<Edition>(
loadSetting(Setting::kEditionSetting, kUnregistered).toInt()); loadSetting(kEditionSetting, kUnregistered).toInt());
} }
m_ServiceEnabled = loadSetting(kServiceEnabled, m_ServiceEnabled).toBool();
try {
// Set the default path of the TLS certificate file in the users DIR // Set the default path of the TLS certificate file in the users DIR
QString certificateFilename = QString certificateFilename =
QString("%1/%2/%3") QString("%1/%2/%3")
.arg(m_CoreInterface.getProfileDir(), "SSL", "Synergy.pem"); .arg(m_CoreInterface.getProfileDir(), "SSL", "Synergy.pem");
m_TLSCertificatePath = m_TlsCertPath = loadSetting(kTlsCertPath, certificateFilename).toString();
loadSetting(Setting::kTLSCertPath, certificateFilename).toString(); m_TlsKeyLength = loadSetting(kTlsKeyLength, "2048").toString();
m_TLSKeyLength = loadSetting(Setting::kTLSKeyLength, "2048").toString(); } catch (const std::exception &e) {
qDebug() << e.what();
qFatal("Failed to get profile dir, unable to configure TLS");
}
if (getCryptoEnabled()) { if (cryptoEnabled()) {
generateCertificate(); generateCertificate();
} }
applyAppSettings();
} }
void AppConfig::saveSettings() { void AppConfig::saveSettings() {
setCommonSetting(Setting::kWizardLastRun, m_WizardLastRun); using enum Setting;
setCommonSetting(Setting::kLoadSystemSettings, m_LoadFromSystemScope);
setCommonSetting(Setting::kGroupClientCheck, m_ClientGroupChecked); setCommonSetting(kWizardLastRun, m_WizardLastRun);
setCommonSetting(Setting::kGroupServerCheck, m_ServerGroupChecked); setCommonSetting(kLoadSystemSettings, m_LoadFromSystemScope);
setCommonSetting(Setting::kGuid, m_guid); setCommonSetting(kGroupClientCheck, m_ClientGroupChecked);
setCommonSetting(Setting::kLicenseRegistryUrl, m_licenseRegistryUrl); setCommonSetting(kGroupServerCheck, m_ServerGroupChecked);
setCommonSetting(Setting::kLicenseNextCheck, m_licenseNextCheck); setCommonSetting(kGuid, m_Guid);
setCommonSetting(kLicenseNextCheck, m_licenseNextCheck);
if (isWritable()) { if (isWritable()) {
setSetting(Setting::kScreenName, m_ScreenName); setSetting(kScreenName, m_ScreenName);
setSetting(Setting::kPort, m_Port); setSetting(kPort, m_Port);
setSetting(Setting::kInterfaceSetting, m_Interface); setSetting(kInterfaceSetting, m_Interface);
setSetting(Setting::kLogLevel, m_LogLevel); setSetting(kLogLevel, m_LogLevel);
setSetting(Setting::kLogToFile, m_LogToFile); setSetting(kLogToFile, m_LogToFile);
setSetting(Setting::kLogFilename, m_LogFilename); setSetting(kLogFilename, m_LogFilename);
setSetting(Setting::kStartedBefore, m_StartedBefore); setSetting(kStartedBefore, m_StartedBefore);
// Refer to enum ElevateMode declaration for insight in to why this setSetting(kElevateModeEnum, static_cast<int>(m_ElevateMode));
// flag is mapped this way setSetting(kEditionSetting, m_Edition);
setSetting(Setting::kElevateModeSetting, m_ElevateMode == ElevateAlways); setSetting(kCryptoEnabled, m_CryptoEnabled);
setSetting(Setting::kElevateModeEnum, static_cast<int>(m_ElevateMode)); setSetting(kAutoHide, m_AutoHide);
setSetting(Setting::kEditionSetting, m_Edition); setSetting(kSerialKey, m_Serialkey);
setSetting(Setting::kCryptoEnabled, m_CryptoEnabled); setSetting(kLastVersion, m_LastVersion);
setSetting(Setting::kAutoHide, m_AutoHide); setSetting(kLastExpireWarningTime, m_LastExpiringWarningTime);
setSetting(Setting::kSerialKey, m_Serialkey); setSetting(kActivationHasRun, m_ActivationHasRun);
setSetting(Setting::kLastVersion, m_lastVersion); setSetting(kMinimizeToTray, m_MinimizeToTray);
setSetting(Setting::kLastExpireWarningTime, m_LastExpiringWarningTime); setSetting(kUseExternalConfig, m_UseExternalConfig);
setSetting(Setting::kActivationHasRun, m_ActivationHasRun); setSetting(kConfigFile, m_ConfigFile);
setSetting(Setting::kMinimizeToTray, m_MinimizeToTray); setSetting(kUseInternalConfig, m_UseInternalConfig);
setSetting(Setting::kUseExternalConfig, m_UseExternalConfig); setSetting(kServerHostname, m_ServerHostname);
setSetting(Setting::kConfigFile, m_ConfigFile); setSetting(kPreventSleep, m_PreventSleep);
setSetting(Setting::kUseInternalConfig, m_UseInternalConfig); setSetting(kLanguageSync, m_LanguageSync);
setSetting(Setting::kServerHostname, m_ServerHostname); setSetting(kInvertScrollDirection, m_InvertScrollDirection);
setSetting(Setting::kPreventSleep, m_PreventSleep); setSetting(kClientHostMode, m_ClientHostMode);
setSetting(Setting::kLanguageSync, m_LanguageSync); setSetting(kServerClientMode, m_ServerClientMode);
setSetting(Setting::kInvertScrollDirection, m_InvertScrollDirection); setSetting(kServiceEnabled, m_ServiceEnabled);
setSetting(Setting::kClientHostMode, m_ClientHostMode); setSetting(kMinimizeOnClose, m_MinimizeOnClose);
setSetting(Setting::kServerClientMode, m_ServerClientMode);
// See enum ElevateMode declaration to understand why this setting is bool
setSetting(kElevateModeSetting, m_ElevateMode == ElevateAlways);
} }
m_unsavedChanges = false; setModified(false);
applyAppSettings();
} }
#ifdef SYNERGY_ENABLE_LICENSING #ifdef SYNERGY_ENABLE_LICENSING
@ -326,10 +296,10 @@ AppConfig &AppConfig::activationHasRun(bool value) {
} }
#endif #endif
QString AppConfig::lastVersion() const { return m_lastVersion; } QString AppConfig::lastVersion() const { return m_LastVersion; }
void AppConfig::setLastVersion(const QString &version) { void AppConfig::setLastVersion(const QString &version) {
setSettingModified(m_lastVersion, version); setSettingModified(m_LastVersion, version);
} }
void AppConfig::setScreenName(const QString &s) { void AppConfig::setScreenName(const QString &s) {
@ -389,9 +359,9 @@ void AppConfig::setLastExpiringWarningTime(int newValue) {
} }
#endif #endif
QString AppConfig::synergysName() const { return m_SynergysName; } QString AppConfig::coreServerName() const { return m_CoreServerName; }
QString AppConfig::synergycName() const { return m_SynergycName; } QString AppConfig::coreClientName() const { return m_CoreClientName; }
ElevateMode AppConfig::elevateMode() { return m_ElevateMode; } ElevateMode AppConfig::elevateMode() { return m_ElevateMode; }
@ -404,7 +374,7 @@ void AppConfig::setCryptoEnabled(bool newValue) {
setSettingModified(m_CryptoEnabled, newValue); setSettingModified(m_CryptoEnabled, newValue);
} }
bool AppConfig::isCryptoAvailable() const { bool AppConfig::cryptoAvailable() const {
bool result{true}; bool result{true};
#ifdef SYNERGY_ENABLE_LICENSING #ifdef SYNERGY_ENABLE_LICENSING
@ -416,19 +386,19 @@ bool AppConfig::isCryptoAvailable() const {
return result; return result;
} }
bool AppConfig::getCryptoEnabled() const { bool AppConfig::cryptoEnabled() const {
return isCryptoAvailable() && m_CryptoEnabled; return cryptoAvailable() && m_CryptoEnabled;
} }
void AppConfig::setAutoHide(bool b) { setSettingModified(m_AutoHide, b); } void AppConfig::setAutoHide(bool b) { setSettingModified(m_AutoHide, b); }
bool AppConfig::getAutoHide() { return m_AutoHide; } bool AppConfig::autoHide() { return m_AutoHide; }
void AppConfig::setMinimizeToTray(bool newValue) { void AppConfig::setMinimizeToTray(bool newValue) {
setSettingModified(m_MinimizeToTray, newValue); setSettingModified(m_MinimizeToTray, newValue);
} }
bool AppConfig::getInvertScrollDirection() const { bool AppConfig::invertScrollDirection() const {
return m_InvertScrollDirection; return m_InvertScrollDirection;
} }
@ -436,17 +406,13 @@ void AppConfig::setLicenseNextCheck(unsigned long long time) {
setSettingModified(m_licenseNextCheck, time); setSettingModified(m_licenseNextCheck, time);
} }
const QString &AppConfig::getLicenseRegistryUrl() const { unsigned long long AppConfig::licenseNextCheck() const {
return m_licenseRegistryUrl;
}
unsigned long long AppConfig::getLicenseNextCheck() const {
return m_licenseNextCheck; return m_licenseNextCheck;
} }
const QString &AppConfig::getGuid() const { return m_guid; } const QString &AppConfig::guid() const { return m_Guid; }
bool AppConfig::getLanguageSync() const { return m_LanguageSync; } bool AppConfig::languageSync() const { return m_LanguageSync; }
void AppConfig::setInvertScrollDirection(bool newValue) { void AppConfig::setInvertScrollDirection(bool newValue) {
setSettingModified(m_InvertScrollDirection, newValue); setSettingModified(m_InvertScrollDirection, newValue);
@ -456,17 +422,17 @@ void AppConfig::setLanguageSync(bool newValue) {
setSettingModified(m_LanguageSync, newValue); setSettingModified(m_LanguageSync, newValue);
} }
bool AppConfig::getPreventSleep() const { return m_PreventSleep; } bool AppConfig::preventSleep() const { return m_PreventSleep; }
bool AppConfig::getClientHostMode() const { bool AppConfig::clientHostMode() const {
return (m_ClientHostMode && getInitiateConnectionFromServer()); return (m_ClientHostMode && initiateConnectionFromServer());
} }
bool AppConfig::getServerClientMode() const { bool AppConfig::serverClientMode() const {
return (m_ServerClientMode && getInitiateConnectionFromServer()); return (m_ServerClientMode && initiateConnectionFromServer());
} }
bool AppConfig::getInitiateConnectionFromServer() const { bool AppConfig::initiateConnectionFromServer() const {
return m_InitiateConnectionFromServer; return m_InitiateConnectionFromServer;
} }
@ -474,57 +440,53 @@ void AppConfig::setPreventSleep(bool newValue) {
setSettingModified(m_PreventSleep, newValue); setSettingModified(m_PreventSleep, newValue);
} }
bool AppConfig::getMinimizeToTray() { return m_MinimizeToTray; } bool AppConfig::minimizeToTray() { return m_MinimizeToTray; }
QString AppConfig::settingName(Setting name) { QString AppConfig::settingName(Setting name) {
auto index = static_cast<int>(name); auto index = static_cast<int>(name);
return m_SynergySettingsName[index]; return m_SettingsName[index];
} }
template <typename T> void AppConfig::setSetting(Setting name, T value) { template <typename T> void AppConfig::setSetting(Setting name, T value) {
ConfigWriter::make()->setSetting(settingName(name), value); m_Config.setSetting(settingName(name), value);
} }
template <typename T> void AppConfig::setCommonSetting(Setting name, T value) { template <typename T> void AppConfig::setCommonSetting(Setting name, T value) {
ConfigWriter::make()->setSetting( m_Config.setSetting(settingName(name), value, Config::Scope::User);
settingName(name), value, ConfigWriter::kUser); m_Config.setSetting(settingName(name), value, Config::Scope::System);
ConfigWriter::make()->setSetting(
settingName(name), value, ConfigWriter::kSystem);
} }
QVariant AppConfig::loadSetting(Setting name, const QVariant &defaultValue) { QVariant AppConfig::loadSetting(Setting name, const QVariant &defaultValue) {
return ConfigWriter::make()->loadSetting(settingName(name), defaultValue); return m_Config.loadSetting(settingName(name), defaultValue);
} }
QVariant QVariant
AppConfig::loadCommonSetting(Setting name, const QVariant &defaultValue) const { AppConfig::loadCommonSetting(Setting name, const QVariant &defaultValue) const {
QVariant result(defaultValue); QVariant result(defaultValue);
QString setting(settingName(name)); QString setting(settingName(name));
auto &writer = *ConfigWriter::make();
if (writer.hasSetting(setting)) { if (m_Config.hasSetting(setting)) {
result = writer.loadSetting(setting, defaultValue); result = m_Config.loadSetting(setting, defaultValue);
} else if (writer.getScope() == ConfigWriter::kSystem) { } else if (m_Config.getScope() == Config::Scope::System) {
if (writer.hasSetting(setting, ConfigWriter::kUser)) { if (m_Config.hasSetting(setting, Config::Scope::User)) {
result = writer.loadSetting(setting, defaultValue, ConfigWriter::kUser); result = m_Config.loadSetting(setting, defaultValue, Config::Scope::User);
} }
} else if (writer.hasSetting(setting, ConfigWriter::kSystem)) { } else if (m_Config.hasSetting(setting, Config::Scope::System)) {
result = writer.loadSetting(setting, defaultValue, ConfigWriter::kSystem); result = m_Config.loadSetting(setting, defaultValue, Config::Scope::System);
} }
return result; return result;
} }
void AppConfig::loadScope(ConfigWriter::Scope scope) { void AppConfig::loadScope(Config::Scope scope) {
auto writer = ConfigWriter::make();
if (writer->getScope() != scope) { if (m_Config.getScope() != scope) {
setDefaultValues(); setDefaultValues();
writer->setScope(scope); m_Config.setScope(scope);
if (writer->hasSetting( if (m_Config.hasSetting(
settingName(Setting::kScreenName), writer->getScope())) { settingName(Setting::kScreenName), m_Config.getScope())) {
// If the user already has settings, then load them up now. // If the user already has settings, then load them up now.
writer->globalLoad(); m_Config.loadAll();
} }
} }
} }
@ -534,9 +496,9 @@ void AppConfig::setDefaultValues() { m_InitiateConnectionFromServer = false; }
void AppConfig::setLoadFromSystemScope(bool value) { void AppConfig::setLoadFromSystemScope(bool value) {
if (value) { if (value) {
loadScope(ConfigWriter::kSystem); loadScope(Config::Scope::System);
} else { } else {
loadScope(ConfigWriter::kUser); loadScope(Config::Scope::User);
} }
/* /*
@ -546,25 +508,23 @@ void AppConfig::setLoadFromSystemScope(bool value) {
m_LoadFromSystemScope = value; m_LoadFromSystemScope = value;
} }
bool AppConfig::isWritable() const { bool AppConfig::isWritable() const { return m_Config.isWritable(); }
return ConfigWriter::make()->isWritable();
}
bool AppConfig::isSystemScoped() const { bool AppConfig::isSystemScoped() const {
return ConfigWriter::make()->getScope() == ConfigWriter::kSystem; return m_Config.getScope() == Config::Scope::System;
} }
bool AppConfig::getServerGroupChecked() const { return m_ServerGroupChecked; } bool AppConfig::serverGroupChecked() const { return m_ServerGroupChecked; }
bool AppConfig::getUseExternalConfig() const { return m_UseExternalConfig; } bool AppConfig::useExternalConfig() const { return m_UseExternalConfig; }
const QString &AppConfig::getConfigFile() const { return m_ConfigFile; } const QString &AppConfig::configFile() const { return m_ConfigFile; }
bool AppConfig::getUseInternalConfig() const { return m_UseInternalConfig; } bool AppConfig::useInternalConfig() const { return m_UseInternalConfig; }
bool AppConfig::getClientGroupChecked() const { return m_ClientGroupChecked; } bool AppConfig::clientGroupChecked() const { return m_ClientGroupChecked; }
QString AppConfig::getServerHostname() const { return m_ServerHostname; } QString AppConfig::serverHostname() const { return m_ServerHostname; }
void AppConfig::setServerGroupChecked(bool newValue) { void AppConfig::setServerGroupChecked(bool newValue) {
setSettingModified(m_ServerGroupChecked, newValue); setSettingModified(m_ServerGroupChecked, newValue);
@ -602,28 +562,43 @@ template <typename T>
void AppConfig::setSettingModified(T &variable, const T &newValue) { void AppConfig::setSettingModified(T &variable, const T &newValue) {
if (variable != newValue) { if (variable != newValue) {
variable = newValue; variable = newValue;
m_unsavedChanges = true; setModified(true);
} }
} }
void AppConfig::setTLSCertPath(const QString &path) { void AppConfig::setTlsCertPath(const QString &path) { m_TlsCertPath = path; }
m_TLSCertificatePath = path;
}
QString AppConfig::getTLSCertPath() const { return m_TLSCertificatePath; } QString AppConfig::tlsCertPath() const { return m_TlsCertPath; }
QString AppConfig::getTLSKeyLength() const { return m_TLSKeyLength; } QString AppConfig::tlsKeyLength() const { return m_TlsKeyLength; }
void AppConfig::setTLSKeyLength(const QString &length) { void AppConfig::setTlsKeyLength(const QString &length) {
if (m_TLSKeyLength != length) { if (m_TlsKeyLength != length) {
m_TLSKeyLength = length; m_TlsKeyLength = length;
generateCertificate(true); generateCertificate(true);
} }
} }
void AppConfig::generateCertificate(bool forceGeneration) const { void AppConfig::generateCertificate(bool forceGeneration) const {
try {
SslCertificate sslCertificate; SslCertificate sslCertificate;
sslCertificate.generateCertificate( sslCertificate.generateCertificate(
getTLSCertPath(), getTLSKeyLength(), forceGeneration); tlsCertPath(), tlsKeyLength(), forceGeneration);
emit sslToggled(); emit sslToggled();
} catch (const std::exception &e) {
qDebug() << e.what();
qFatal("Failed to configure TLS");
} }
}
void AppConfig::setServiceEnabled(bool enabled) {
setSettingModified(m_ServiceEnabled, enabled);
}
bool AppConfig::serviceEnabled() const { return m_ServiceEnabled; }
void AppConfig::setMinimizeOnClose(bool minimize) {
setSettingModified(m_MinimizeOnClose, minimize);
}
bool AppConfig::minimizeOnClose() const { return m_MinimizeOnClose; }

View file

@ -1,6 +1,6 @@
/* /*
* synergy -- mouse and keyboard sharing utility * synergy -- mouse and keyboard sharing utility
* Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2012 Symless Ltd.
* Copyright (C) 2008 Volker Lanz (vl@fidra.de) * 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
@ -16,165 +16,122 @@
* 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(APPCONFIG_H) #pragma once
#define APPCONFIG_H #include "CommonConfig.h"
#include "Config.h"
#include "ConfigBase.h"
#include "ConfigWriter.h"
#include "CoreInterface.h" #include "CoreInterface.h"
#include "ElevateMode.h" #include "ElevateMode.h"
#include "LicenseManager.h"
#include "shared/EditionType.h"
#include <QObject> #include <QObject>
#include <QString> #include <QString>
#include <QVariant> #include <QVariant>
#include <mutex> #include <mutex>
#include <shared/EditionType.h>
// this should be incremented each time a new page is added. this is
// saved to settings when the user finishes running the wizard. if
// the saved wizard version is lower than this number, the wizard
// will be displayed. each version incrememnt should be described
// here...
//
// 1: first version
// 2: added language page
// 3: added premium page and removed
// 4: ssl plugin 'ns' v1.0
// 5: ssl plugin 'ns' v1.1
// 6: ssl plugin 'ns' v1.2
// 7: serial key activation
// 8: Visual Studio 2015 support
//
const int kWizardVersion = 8;
class QSettings; class QSettings;
class SettingsDialog; class SettingsDialog;
class ServerConfig;
class LicenseRegister; class LicenseRegister;
class ServerConfig;
class LicenseManager;
class ActivationDialog;
class LicenseRegistry;
enum class ProcessMode { kService, kDesktop }; enum class ProcessMode { kService, kDesktop };
class AppConfig : public QObject, public GUI::Config::ConfigBase { const ElevateMode kDefaultElevateMode = ElevateAsNeeded;
#if defined(Q_OS_WIN)
const ProcessMode kDefaultProcessMode = ProcessMode::kService;
#else
const ProcessMode kDefaultProcessMode = ProcessMode::kDesktop;
#endif
/// @brief Reads and writes application specific settings
class AppConfig : public QObject, public synergy::gui::CommonConfig {
Q_OBJECT Q_OBJECT
friend class SettingsDialog; friend class SettingsDialog;
friend class MainWindow; friend class MainWindow;
friend class SetupWizard; friend class SetupWizard;
friend class ServerConfig; friend class ServerConfig;
friend class LicenseManager;
friend class ActivationDialog;
friend class LicenseRegistry;
public: public:
explicit AppConfig(bool globalLoad = true); explicit AppConfig();
/// @brief Underlying configuration reader/writer
synergy::gui::Config &config();
void saveSettings() override;
void applyAppSettings() const;
/// @brief Generates TLS certificate
/// @param [in] bool forceGeneration Generate certificate even if it's exists.
void generateCertificate(bool forceGeneration = false) const;
/**
* Getters
*/
bool isWritable() const; bool isWritable() const;
bool isSystemScoped() const; bool isSystemScoped() const;
const QString &screenName() const; const QString &screenName() const;
int port() const; int port() const;
const QString &networkInterface() const; const QString &networkInterface() const;
int logLevel() const; int logLevel() const;
bool logToFile() const; bool logToFile() const;
const QString &logFilename() const; const QString &logFilename() const;
const QString logFilenameCmd() const;
QString logLevelText() const; QString logLevelText() const;
ProcessMode processMode() const; ProcessMode processMode() const;
bool wizardShouldRun() const; bool wizardShouldRun() const;
bool startedBefore() const; bool startedBefore() const;
QString coreServerName() const;
#ifdef SYNERGY_ENABLE_LICENSING QString coreClientName() const;
void setEdition(Edition); QString logDir() const;
Edition edition() const; void persistLogDir() const;
void setSerialKey(const QString &serial);
void clearSerialKey();
QString serialKey() const;
int lastExpiringWarningTime() const;
void setLastExpiringWarningTime(int t);
#endif
QString synergysName() const;
QString synergycName() const;
QString synergyProgramDir() const;
QString synergyLogDir() const;
void persistLogDir();
ElevateMode elevateMode(); ElevateMode elevateMode();
bool cryptoAvailable() const;
bool isCryptoAvailable() const; bool cryptoEnabled() const;
void setCryptoEnabled(bool e); bool autoHide();
bool getCryptoEnabled() const; bool invertScrollDirection() const;
unsigned long long licenseNextCheck() const;
void setAutoHide(bool b); const QString &guid() const;
bool getAutoHide(); bool languageSync() const;
void setInvertScrollDirection(bool b); bool preventSleep() const;
bool getInvertScrollDirection() const; bool clientHostMode() const;
void setLicenseNextCheck(unsigned long long); bool serverClientMode() const;
const QString &getLicenseRegistryUrl() const; bool initiateConnectionFromServer() const;
unsigned long long getLicenseNextCheck() const; bool serverGroupChecked() const;
const QString &getGuid() const; bool useExternalConfig() const;
void setLanguageSync(bool b); const QString &configFile() const;
bool getLanguageSync() const; bool useInternalConfig() const;
void setPreventSleep(bool b); bool clientGroupChecked() const;
bool getPreventSleep() const; QString serverHostname() const;
bool getClientHostMode() const; QString lastVersion() const;
bool getServerClientMode() const; bool serviceEnabled() const;
bool getInitiateConnectionFromServer() const; bool minimizeToTray();
#ifdef SYNERGY_ENABLE_LICENSING bool minimizeOnClose() const;
bool activationHasRun() const;
AppConfig &activationHasRun(bool value);
#endif
/// @brief Sets the user preference to load from SystemScope.
/// @param [in] value
/// True - This will set the variable and load the global scope
/// settings. False - This will set the variable and load the user
/// scope settings.
void setLoadFromSystemScope(bool value);
bool getServerGroupChecked() const;
bool getUseExternalConfig() const;
const QString &getConfigFile() const;
bool getUseInternalConfig() const;
bool getClientGroupChecked() const;
QString getServerHostname() const;
/// @brief Gets the current TLS certificate path /// @brief Gets the current TLS certificate path
/// @return QString The path to the cert /// @return QString The path to the cert
QString getTLSCertPath() const; QString tlsCertPath() const;
/// @brief Get the key length to be used for the private key of a TLS cert /// @brief Get the key length to be used for the private key of a TLS cert
/// @return QString The key length in bits /// @return QString The key length in bits
QString getTLSKeyLength() const; QString tlsKeyLength() const;
void setServerGroupChecked(bool); #ifdef SYNERGY_ENABLE_LICENSING
void setUseExternalConfig(bool); Edition edition() const;
void setConfigFile(const QString &); QString serialKey() const;
void setUseInternalConfig(bool); int lastExpiringWarningTime() const;
void setClientGroupChecked(bool); bool activationHasRun() const;
void setServerHostname(const QString &); #endif
void setClientHostMode(bool newValue);
void setServerClientMode(bool newValue);
/// @brief Set the path to the TLS/SSL certificate file that will be used
/// @param [in] path The path to the Certificate
void setTLSCertPath(const QString &path);
/// @brief Sets the key length of the private key to use in a TLS connection
/// @param [in] QString length The key length eg: 1024, 2048, 4096
void setTLSKeyLength(const QString &length);
QString lastVersion() const;
void setMinimizeToTray(bool b);
bool getMinimizeToTray();
void saveSettings() override;
void setLastVersion(const QString &version);
/// @brief Generates TLS certificate
/// @param [in] bool forceGeneration Generate certificate even if it's exists.
void generateCertificate(bool forceGeneration = false) const;
protected: protected:
/// @brief The enumeration to easily access the names of the setting inside
/// m_SynergySettingsName
enum class Setting { enum class Setting {
kScreenName, kScreenName,
kPort, kPort,
@ -202,8 +159,8 @@ protected:
kUseInternalConfig, kUseInternalConfig,
kGroupClientCheck, kGroupClientCheck,
kServerHostname, kServerHostname,
kTLSCertPath, kTlsCertPath,
kTLSKeyLength, kTlsKeyLength,
kPreventSleep, kPreventSleep,
kLanguageSync, kLanguageSync,
kInvertScrollDirection, kInvertScrollDirection,
@ -212,9 +169,20 @@ protected:
kLicenseNextCheck, kLicenseNextCheck,
kInitiateConnectionFromServer, kInitiateConnectionFromServer,
kClientHostMode, kClientHostMode,
kServerClientMode kServerClientMode,
kServiceEnabled,
kMinimizeOnClose
}; };
static QString settingName(AppConfig::Setting name);
/// @brief Loads the setting from the current scope
void loadSettings() override;
/**
* Setters
*/
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);
@ -224,70 +192,48 @@ protected:
void setWizardHasRun(); void setWizardHasRun();
void setStartedBefore(bool b); void setStartedBefore(bool b);
void setElevateMode(ElevateMode em); void setElevateMode(ElevateMode em);
void setCryptoEnabled(bool e);
void setEdition(Edition);
void setSerialKey(const QString &serial);
void clearSerialKey();
void setLastExpiringWarningTime(int t);
void setAutoHide(bool b);
void setInvertScrollDirection(bool b);
void setLicenseNextCheck(unsigned long long);
void setLanguageSync(bool b);
void setPreventSleep(bool b);
void setServerGroupChecked(bool);
void setUseExternalConfig(bool);
void setConfigFile(const QString &);
void setUseInternalConfig(bool);
void setClientGroupChecked(bool);
void setServerHostname(const QString &);
void setClientHostMode(bool newValue);
void setServerClientMode(bool newValue);
AppConfig &activationHasRun(bool value);
void setMinimizeToTray(bool b);
void setLastVersion(const QString &version);
void setServiceEnabled(bool enabled);
void setMinimizeOnClose(bool minimize);
/// @brief loads the setting from the current scope /// @brief Sets the user preference to load from SystemScope.
/// @param ignoreSystem should the load feature ignore the globalScope setting /// @param [in] value
/// that was saved /// True - This will set the variable and load the global scope
void loadSettings() override; /// settings. False - This will set the variable and load the user
static QString settingName(AppConfig::Setting name); /// scope settings.
void setLoadFromSystemScope(bool value);
/// @brief Set the path to the TLS/SSL certificate file that will be used
/// @param [in] path The path to the Certificate
void setTlsCertPath(const QString &path);
/// @brief Sets the key length of the private key to use in a TLS connection
/// @param [in] QString length The key length eg: 1024, 2048, 4096
void setTlsKeyLength(const QString &length);
private: private:
QString m_ScreenName; /// @brief Loads config from the underlying reader/writer
int m_Port; void load();
QString m_Interface;
int m_LogLevel;
bool m_LogToFile;
QString m_LogFilename;
int m_WizardLastRun;
ProcessMode m_ProcessMode;
bool m_StartedBefore;
ElevateMode m_ElevateMode;
Edition m_Edition;
QString m_ActivateEmail;
bool m_CryptoEnabled;
bool m_AutoHide;
QString m_Serialkey;
QString m_lastVersion;
QString m_guid;
QString m_licenseRegistryUrl;
unsigned long long m_licenseNextCheck;
int m_LastExpiringWarningTime;
bool m_ActivationHasRun;
bool m_MinimizeToTray;
bool m_InvertScrollDirection = false;
bool m_LanguageSync = true;
bool m_PreventSleep = false;
bool m_InitiateConnectionFromServer = false;
bool m_ClientHostMode = true;
bool m_ServerClientMode = true;
bool m_ServerGroupChecked;
bool m_UseExternalConfig;
QString m_ConfigFile;
bool m_UseInternalConfig;
bool m_ClientGroupChecked;
QString m_ServerHostname;
QString m_TLSCertificatePath; /// @brief The path to the TLS certificate file
QString m_TLSKeyLength; /// @brief The key length of the TLS cert to make
bool m_LoadFromSystemScope; /// @brief should the setting be loaded from
/// SystemScope
/// If the user has settings but this is
/// true then system settings will be
/// loaded instead of the users
CoreInterface m_CoreInterface;
static const char m_SynergysName[];
static const char m_SynergycName[];
static const char m_SynergyLogDir[];
/// @brief Contains the string values of the settings names that will be saved
static const char *m_SynergySettingsName[];
/// @brief Contains the name of the default configuration filename
static const char m_SynergyConfigName[];
/// @brief Sets the value of a setting /// @brief Sets the value of a setting
/// @param [in] name The Setting to be saved /// @param [in] name The Setting to be saved
@ -312,12 +258,6 @@ private:
QVariant loadCommonSetting( QVariant loadCommonSetting(
AppConfig::Setting name, const QVariant &defaultValue = QVariant()) const; AppConfig::Setting name, const QVariant &defaultValue = QVariant()) const;
/// @brief As the settings will be accessible by multiple objects this lock
/// will ensure that
/// it cant be modified by more that one object at a time if the
/// setting is being switched from system to user.
std::mutex m_settings_lock;
/// @brief Sets the setting in the config checking if it has changed and /// @brief Sets the setting in the config checking if it has changed and
/// flagging that settings /// flagging that settings
/// needs to be saved if the setting was different /// needs to be saved if the setting was different
@ -327,15 +267,79 @@ 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(GUI::Config::ConfigWriter::Scope scope); void loadScope(synergy::gui::Config::Scope scope);
/// @brief This function sets default values /// @brief This function sets default values
/// for settings that shouldn't be copied from between scopes. /// for settings that shouldn't be copied from between scopes.
void setDefaultValues(); void setDefaultValues();
synergy::gui::Config m_Config;
CoreInterface m_CoreInterface;
QString m_ScreenName = "";
int m_Port = 24800;
QString m_Interface = "";
int m_LogLevel = 0;
bool m_LogToFile = false;
QString m_LogFilename = "";
int m_WizardLastRun = 0;
bool m_StartedBefore = false;
ElevateMode m_ElevateMode = kDefaultElevateMode;
Edition m_Edition = Edition::kUnregistered;
QString m_ActivateEmail = "";
bool m_CryptoEnabled = false;
bool m_AutoHide = false;
QString m_Serialkey = "";
QString m_LastVersion = "";
QString m_Guid = "";
unsigned long long m_licenseNextCheck = 0;
int m_LastExpiringWarningTime = 0;
bool m_ActivationHasRun = false;
bool m_MinimizeToTray = true;
bool m_InvertScrollDirection = false;
bool m_LanguageSync = true;
bool m_PreventSleep = false;
bool m_InitiateConnectionFromServer = false;
bool m_ClientHostMode = true;
bool m_ServerClientMode = true;
bool m_ServerGroupChecked = false;
bool m_UseExternalConfig = false;
QString m_ConfigFile = "";
bool m_UseInternalConfig = false;
bool m_ClientGroupChecked = false;
QString m_ServerHostname = "";
bool m_ServiceEnabled = kDefaultProcessMode == ProcessMode::kService;
bool m_MinimizeOnClose = true;
/// @brief The path to the TLS certificate file
QString m_TlsCertPath = "";
/// @brief The key length of the TLS cert to make
QString m_TlsKeyLength = "";
/// @brief should the setting be loaded from
/// SystemScope
/// If the user has settings but this is
/// true then system settings will be
/// loaded instead of the users
bool m_LoadFromSystemScope = false;
/// @brief As the settings will be accessible by multiple objects this lock
/// will ensure that
/// it cant be modified by more that one object at a time if the
/// setting is being switched from system to user.
std::mutex m_settings_lock;
static const char m_CoreServerName[];
static const char m_CoreClientName[];
static const char m_LogDir[];
/// @brief Contains the string values of the settings names that will be saved
static const char *const m_SettingsName[];
/// @brief Contains the name of the default configuration filename
static const char m_ConfigFilename[];
signals: signals:
void sslToggled() const; void sslToggled() const;
void screenNameChanged() const; void screenNameChanged() const;
}; };
#endif

View file

@ -7,7 +7,7 @@
<x>0</x> <x>0</x>
<y>0</y> <y>0</y>
<width>400</width> <width>400</width>
<height>165</height> <height>246</height>
</rect> </rect>
</property> </property>
<property name="windowTitle"> <property name="windowTitle">
@ -17,9 +17,7 @@
<item> <item>
<widget class="QLabel" name="label"> <widget class="QLabel" name="label">
<property name="text"> <property name="text">
<string>Are you sure? <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;You'll need to purchase a license to use this build of Synergy.&lt;/p&gt;&lt;p&gt;&lt;a href=&quot;https://symless.com/synergy/purchase?source=gui&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#007af4;&quot;&gt;Purchase Synergy&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;&lt;p&gt;If you'd prefer to use the community edition instead, visit us on GitHub.&lt;/p&gt;&lt;p&gt;&lt;a href=&quot;https://github.com/symless/synergy-core&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#007af4;&quot;&gt;GitHub project&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;&lt;p&gt;Would you like to go back and enter a serial key?&lt;/p&gt;&lt;p&gt;Choosing 'No' will quit Synergy.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
If you don't activate Synergy you'll be missing out on some great features.</string>
</property> </property>
<property name="wordWrap"> <property name="wordWrap">
<bool>true</bool> <bool>true</bool>
@ -30,14 +28,17 @@ If you don't activate Synergy you'll be missing out on some great features.</str
</widget> </widget>
</item> </item>
<item> <item>
<widget class="QLabel" name="label_2"> <spacer name="verticalSpacer">
<property name="text"> <property name="orientation">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;a href=&quot;https://symless.com/pricing?source=gui&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#0000ff;&quot;&gt;Buy now&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string> <enum>Qt::Vertical</enum>
</property> </property>
<property name="openExternalLinks"> <property name="sizeHint" stdset="0">
<bool>true</bool> <size>
<width>20</width>
<height>40</height>
</size>
</property> </property>
</widget> </spacer>
</item> </item>
<item> <item>
<widget class="QDialogButtonBox" name="buttonBox"> <widget class="QDialogButtonBox" name="buttonBox">

View file

@ -59,13 +59,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().getServerHostname()); QHostAddress address(m_parent.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().getServerHostname()); .arg(m_parent.appConfig().serverHostname());
} }
} }

View file

@ -18,7 +18,6 @@
#include "CommandProcess.h" #include "CommandProcess.h"
#include <QProcess> #include <QProcess>
#include <stdexcept>
CommandProcess::CommandProcess( CommandProcess::CommandProcess(
QString cmd, QStringList arguments, QString input) QString cmd, QStringList arguments, QString input)
@ -32,7 +31,8 @@ QString CommandProcess::run() {
process.start(m_Command, m_Arguments); process.start(m_Command, m_Arguments);
bool success = process.waitForStarted(); bool success = process.waitForStarted();
QString output, error; QString output;
QString error;
if (success) { if (success) {
if (!m_Input.isEmpty()) { if (!m_Input.isEmpty()) {
process.write(m_Input.toStdString().c_str()); process.write(m_Input.toStdString().c_str());
@ -45,12 +45,11 @@ QString CommandProcess::run() {
} }
} }
int code = process.exitCode(); if (int code = process.exitCode(); !success || code != 0) {
if (!error.isEmpty() || !success || code != 0) { qFatal(
throw std::runtime_error(QString("Code: %1\nError: %2") "Command failed: %s %s\nCode: %d\nError: %s", qUtf8Printable(m_Command),
.arg(process.exitCode()) qUtf8Printable(m_Arguments.join(" ")), code,
.arg(error.isEmpty() ? "Unknown" : error) error.isEmpty() ? "None" : qUtf8Printable(error));
.toStdString());
} }
emit finished(); emit finished();

View file

@ -0,0 +1,36 @@
/*
* synergy -- mouse and keyboard sharing utility
* Copyright (C) 2020 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
namespace synergy::gui {
/// @brief Common configuration interface
class CommonConfig {
public:
CommonConfig() = default;
virtual ~CommonConfig() = default;
virtual void loadSettings() = 0;
virtual void saveSettings() = 0;
bool modified() const { return m_modified; }
void setModified(bool modified) { m_modified = modified; }
private:
bool m_modified = false;
};
} // namespace synergy::gui

View file

@ -1,6 +1,6 @@
/* /*
* synergy -- mouse and keyboard sharing utility * synergy -- mouse and keyboard sharing utility
* Copyright (C) 2020-2020 Symless Ltd. * Copyright (C) 2020 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,21 +15,21 @@
* 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 <cassert> #include "Config.h"
#include "CommonConfig.h"
#include <QCoreApplication> #include <QCoreApplication>
#include <QFile> #include <QFile>
#include <cassert>
#include <memory>
#include "ConfigBase.h" namespace synergy::gui {
#include "ConfigWriter.h"
namespace {
QString getSystemSettingPath() { QString getSystemSettingPath() {
const QString settingFilename("SystemConfig.ini"); const QString settingFilename("SystemConfig.ini");
QString path; QString path;
#if defined(Q_OS_WIN) #if defined(Q_OS_WIN)
// Program file
path = QCoreApplication::applicationDirPath() + "\\"; path = QCoreApplication::applicationDirPath() + "\\";
#elif defined(Q_OS_DARWIN) #elif defined(Q_OS_DARWIN)
// Global preferances dir // Global preferances dir
@ -46,7 +46,7 @@ QString getSystemSettingPath() {
} }
#if defined(Q_OS_WIN) #if defined(Q_OS_WIN)
void loadOldSystemSettings(QSettings &settings) { void loadWindowsLegacy(QSettings &settings) {
if (!QFile(settings.fileName()).exists()) { if (!QFile(settings.fileName()).exists()) {
QSettings::setPath( QSettings::setPath(
QSettings::IniFormat, QSettings::SystemScope, "SystemConfig.ini"); QSettings::IniFormat, QSettings::SystemScope, "SystemConfig.ini");
@ -68,147 +68,114 @@ void loadOldSystemSettings(QSettings &settings) {
} }
#endif #endif
} // namespace Config::Config() {
namespace GUI {
namespace Config {
// Assignment of static variable
ConfigWriter *ConfigWriter::s_pConfiguration = nullptr;
ConfigWriter *ConfigWriter::make() {
// Only one ConfigWriter can exist at any one time (Singleton)
if (!s_pConfiguration) {
s_pConfiguration = new ConfigWriter();
}
return s_pConfiguration;
}
ConfigWriter::ConfigWriter() {
QSettings::setPath( QSettings::setPath(
QSettings::Format::IniFormat, QSettings::Scope::SystemScope, QSettings::Format::IniFormat, QSettings::Scope::SystemScope,
getSystemSettingPath()); getSystemSettingPath());
// Config will default to User settings if they exist, // Config will default to User settings if they exist,
// otherwise it will load System setting and save them to User settings // otherwise it will load System setting and save them to User settings
m_pSettingsSystem = new QSettings( m_pSystemSettings = std::make_unique<QSettings>(
QSettings::Format::IniFormat, QSettings::Scope::SystemScope, QSettings::Format::IniFormat, QSettings::Scope::SystemScope,
QCoreApplication::organizationName(), QCoreApplication::organizationName(),
QCoreApplication::applicationName()); QCoreApplication::applicationName());
// default to user scope.
// if we set the scope specifically then we also have to set the application
// name and the organisation name which breaks backwards compatibility.
m_pUserSettings = std::make_unique<QSettings>();
load();
}
Config::~Config() {
while (!m_pReceievers.empty()) {
m_pReceievers.pop_back();
}
}
void Config::load() {
#if defined(Q_OS_WIN) #if defined(Q_OS_WIN)
// This call is needed for backwardcapability with old settings. // This call is needed for backwardcapability with old settings.
loadOldSystemSettings(*m_pSettingsSystem); loadWindowsLegacy(*m_pSystemSettings);
#endif #endif
// defaults to user scope, if we set the scope specifically then we also have
// to set
// the application name and the organisation name which breaks backwards
// compatibility See #6730
m_pSettingsUser = new QSettings();
// Set scope to user for initially
m_pSettingsCurrent = m_pSettingsUser;
} }
void ConfigWriter::destroy() { destroy(s_pConfiguration); } bool Config::hasSetting(const QString &name, Scope scope) const {
ConfigWriter::~ConfigWriter() {
while (!m_pCallerList.empty()) {
m_pCallerList.pop_back();
}
m_pSettingsCurrent = nullptr; // this only references other pointers
destroy(m_pSettingsSystem);
destroy(m_pSettingsUser);
}
bool ConfigWriter::hasSetting(const QString &name, Scope scope) const {
switch (scope) { switch (scope) {
case kUser: case Scope::User:
return m_pSettingsUser->contains(name); return m_pUserSettings->contains(name);
case kSystem: case Scope::System:
return m_pSettingsSystem->contains(name); return m_pSystemSettings->contains(name);
default: default:
return m_pSettingsCurrent->contains(name); return currentSettings()->contains(name);
} }
} }
bool ConfigWriter::isWritable() const { bool Config::isWritable() const { return currentSettings()->isWritable(); }
return m_pSettingsCurrent->isWritable();
}
QVariant ConfigWriter::loadSetting( QVariant Config::loadSetting(
const QString &name, const QVariant &defaultValue, Scope scope) { const QString &name, const QVariant &defaultValue, Scope scope) const {
switch (scope) { switch (scope) {
case kUser: case Scope::User:
return m_pSettingsUser->value(name, defaultValue); return m_pUserSettings->value(name, defaultValue);
case kSystem: case Scope::System:
return m_pSettingsSystem->value(name, defaultValue); return m_pSystemSettings->value(name, defaultValue);
default: default:
return m_pSettingsCurrent->value(name, defaultValue); return currentSettings()->value(name, defaultValue);
} }
} }
void ConfigWriter::setScope(ConfigWriter::Scope scope) { void Config::setScope(Config::Scope scope) { m_CurrentScope = scope; }
if (m_CurrentScope != scope) {
m_CurrentScope = scope;
switch (scope) {
case kUser:
m_pSettingsCurrent = m_pSettingsUser;
break;
case kSystem:
m_pSettingsCurrent = m_pSettingsSystem;
break;
default:
// setScope should never be kCurrent
assert(scope);
}
}
}
ConfigWriter::Scope ConfigWriter::getScope() const { return m_CurrentScope; } Config::Scope Config::getScope() const { return m_CurrentScope; }
void ConfigWriter::globalLoad() { void Config::loadAll() {
for (auto &i : m_pCallerList) { for (auto &i : m_pReceievers) {
i->loadSettings(); i->loadSettings();
} }
} }
void ConfigWriter::globalSave() { void Config::saveAll() {
// Save if there are any unsaved changes otherwise skip // Save if there are any unsaved changes otherwise skip
if (unsavedChanges()) { if (unsavedChanges()) {
for (auto &i : m_pCallerList) { for (auto &i : m_pReceievers) {
i->saveSettings(); i->saveSettings();
} }
m_pSettingsUser->sync(); m_pUserSettings->sync();
m_pSettingsSystem->sync(); m_pSystemSettings->sync();
m_unsavedChanges = false; m_unsavedChanges = false;
} }
} }
QSettings &ConfigWriter::settings() { return *m_pSettingsCurrent; } QSettings *Config::currentSettings() const {
if (m_CurrentScope == Scope::User) {
void ConfigWriter::registerClass(ConfigBase *receiver) { return m_pUserSettings.get();
m_pCallerList.push_back(receiver); } else {
return m_pSystemSettings.get();
}
} }
bool ConfigWriter::unsavedChanges() const { void Config::registerReceiever(CommonConfig *receiver) {
m_pReceievers.push_back(receiver);
}
bool Config::unsavedChanges() const {
if (m_unsavedChanges) { if (m_unsavedChanges) {
return true; return true;
} }
for (const auto &i : m_pCallerList) { for (const auto &i : m_pReceievers) {
if (i->modified()) { if (i->modified()) {
// If any class returns true there is no point checking more
return true; return true;
} }
} }
// If this line is reached no class has unsaved changes
return false; return false;
} }
void ConfigWriter::markUnsaved() { m_unsavedChanges = true; } void Config::markUnsaved() { m_unsavedChanges = true; }
} // namespace Config
} // namespace GUI } // namespace synergy::gui

115
src/gui/src/Config.h Normal file
View file

@ -0,0 +1,115 @@
/*
* synergy -- mouse and keyboard sharing utility
* Copyright (C) 2020 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 <QSettings>
#include <QVariant>
#include <memory>
namespace synergy::gui {
class CommonConfig;
/// @brief A general config reader and writer for user and gloabl settings
class Config : private QObject {
public:
enum class Scope { Current, System, User };
explicit Config();
~Config() override;
/// @brief Checks if the setting exists
/// @param [in] name The name of the setting to check
/// @param [in] scope The scope to search in
/// @return bool True if the current scope has the named setting
bool hasSetting(const QString &name, Scope scope = Scope::Current) const;
/// @brief Checks if the current scope settings writable
/// @return bool True if the current scope writable
bool isWritable() const;
/// @brief Sets the value of a setting
/// @param [in] name The Setting to be saved
/// @param [in] value The Value to be saved (Templated)
/// @param [in] scope The scope to get the value from, default is current
/// scope
template <typename T>
void setSetting(const QString &name, T value, Scope scope = Scope::Current);
/// @brief Loads a setting
/// @param [in] name The setting to be loaded
/// @param [in] defaultValue The default value of the setting
/// @param [in] scope The scope to get the value from, default is current
/// scope
QVariant loadSetting(
const QString &name, const QVariant &defaultValue = QVariant(),
Scope scope = Scope::Current) const;
/// @brief Changes the setting save and load location between System and User
/// scope
/// @param [in] scope The scope to set
void setScope(Scope scope = Scope::User);
/// @brief Get the current scope the settings are loading and save from.
/// @return Scope An enum defining the current scope
Scope getScope() const;
/// @brief trigger a config load across all registered classes
void loadAll();
/// @brief trigger a config save across all registered classes
void saveAll();
/// @brief Returns the current scopes settings object
/// If more specialize control into the settings is needed this can
/// provide direct access to the settings file handler
/// @return QSettings The Settings object as a reference
QSettings *currentSettings() const;
/// @brief This marks the settings as unsaved if the settings() was used to
/// directly affect the config file
void markUnsaved();
/// @brief Register a class to receives requests to save and load settings
void registerReceiever(CommonConfig *receiver);
/// @brief Checks if any registered class has any unsaved changes
/// @return bool True if any registered class has unsaved changes
bool unsavedChanges() const;
private:
void load();
Scope m_CurrentScope = Scope::User;
std::unique_ptr<QSettings> m_pUserSettings;
std::unique_ptr<QSettings> m_pSystemSettings;
/// @brief Receivers of load/save callbacks
std::list<CommonConfig *> m_pReceievers;
/// @brief Is set to true when settings are changed
bool m_unsavedChanges = false;
};
template <typename T>
void Config::setSetting(const QString &name, T value, Scope scope) {
currentSettings()->setValue(name, value);
m_unsavedChanges = true;
}
} // namespace synergy::gui

View file

@ -1,51 +0,0 @@
/*
* synergy -- mouse and keyboard sharing utility
* Copyright (C) 2020 - 2020 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/>.
*/
#ifndef SYNERGY_CORE_CONFIGBASE_H
#define SYNERGY_CORE_CONFIGBASE_H
namespace GUI {
namespace Config {
///@brief This abstract class will be used by all classes that use the
/// ConfigWriter
/// to allow global saving and loading
class ConfigBase {
public:
ConfigBase() = default;
virtual ~ConfigBase() = default;
/// @brief The function that is called when the settings need to be loaded
/// from file
virtual void loadSettings() = 0;
/// @brief The function that is called when the settings need to be saved to
/// file
virtual void saveSettings() = 0;
/// @brief Returns true if the class has marked itself with having unsaved
/// changes
bool modified() const { return m_unsavedChanges; }
protected:
/// @brief Does the class have unsaved changes in it.
bool m_unsavedChanges = false;
};
} // namespace Config
} // namespace GUI
#endif // SYNERGY_CORE_CONFIGBASE_H

View file

@ -1,157 +0,0 @@
/*
* synergy -- mouse and keyboard sharing utility
* Copyright (C) 2020 - 2020 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/>.
*/
#ifndef SYNERGY_CORE_CONFIGWRITER_H
#define SYNERGY_CORE_CONFIGWRITER_H
#include <QSettings>
#include <QVariant>
/// @brief Contains GUI code
namespace GUI {
/// @brief Contains Configuration code
namespace Config {
// Forward declare the class referenced by pointer
class ConfigBase;
class ConfigWriter : private QObject {
public:
/// @brief the public way to construct the configuration calls
/// The pointer returned is owned by this class and should not be
/// stored by other classes.
static ConfigWriter *make();
/// @brief the public way to destroy the configuration class
static void destroy();
~ConfigWriter() override;
///@brief An Enumeration of all the scopes available
enum Scope { kCurrent, kSystem, kUser };
/// @brief Checks if the setting exists
/// @param [in] name The name of the setting to check
/// @param [in] scope The scope to search in
/// @return bool True if the current scope has the named setting
bool hasSetting(const QString &name, Scope scope = kCurrent) const;
/// @brief Checks if the current scope settings writable
/// @return bool True if the current scope writable
bool isWritable() const;
/// @brief Sets the value of a setting
/// @param [in] name The Setting to be saved
/// @param [in] value The Value to be saved (Templated)
/// @param [in] scope The scope to get the value from, default is current
/// scope
template <typename T>
void setSetting(const QString &name, T value, Scope scope = kCurrent);
/// @brief Loads a setting
/// @param [in] name The setting to be loaded
/// @param [in] defaultValue The default value of the setting
/// @param [in] scope The scope to get the value from, default is current
/// scope
QVariant loadSetting(
const QString &name, const QVariant &defaultValue = QVariant(),
Scope scope = kCurrent);
/// @brief Changes the setting save and load location between System and User
/// scope
/// @param [in] scope The scope to set
void setScope(Scope scope = kUser);
/// @brief Get the current scope the settings are loading and save from.
/// @return Scope An enum defining the current scope
Scope getScope() const;
/// @brief trigger a config load across all registered classes
void globalLoad();
/// @brief trigger a config save across all registered classes
void globalSave();
/// @brief Returns the current scopes settings object
/// If more specialize control into the settings is needed this can
/// provide direct access to the settings file handler
/// @return QSettings The Settings object as a reference
QSettings &settings();
/// @brief This marks the settings as unsaved if the settings() was used to
/// directly affect the config file
void markUnsaved();
/// @brief Register a class to receives globalLoad and globalSave events
/// @param [in] ConfigBase The class that will receive the events
void registerClass(ConfigBase *receiver);
/// @brief Checks if any registered class has any unsaved changes
/// @return bool True if any registered class has unsaved changes
bool unsavedChanges() const;
protected:
Scope m_CurrentScope = kUser; /// @brief The current scope of the settings
QSettings *m_pSettingsCurrent =
nullptr; /// @brief The currently active settings
QSettings *m_pSettingsUser = nullptr; /// @brief The user specific settings
QSettings *m_pSettingsSystem = nullptr; /// @brief The system wide settings
private:
/// @brief Contains a list all all classes that hook into the writer.
/// This allows all classes that save settings to be called an updated
/// on a save and reload by any other class
std::list<ConfigBase *> m_pCallerList;
/// @brief if this class modified settings then set the flag
bool m_unsavedChanges = false;
/// @brief The constructor, as this is a singolton we want to control who can
/// call the constructor
ConfigWriter();
/// @brief the pointer of the ConfigWriter for singolton use
static ConfigWriter *s_pConfiguration;
/// @brief deletes pointers and sets the value to null
template <class T> static inline void destroy(T *&p) {
delete p;
p = 0;
}
};
// Implementation of a template function needs to be visible to all calls thus
// is must be in the header Moved so its not bulking out the class definition
template <typename T>
void ConfigWriter::setSetting(const QString &name, T value, Scope scope) {
switch (scope) {
case kUser:
m_pSettingsUser->setValue(name, value);
break;
case kSystem:
m_pSettingsSystem->setValue(name, value);
break;
default:
m_pSettingsCurrent->setValue(name, value);
break;
}
m_unsavedChanges = true;
}
} // namespace Config
} // namespace GUI
#endif // SYNERGY_CORE_CONFIGWRITER_H

View file

@ -18,13 +18,11 @@
#include "CoreInterface.h" #include "CoreInterface.h"
#include "CommandProcess.h" #include "CommandProcess.h"
#include "QUtility.h"
#include <QCoreApplication> #include <QCoreApplication>
#include <QDir> #include <QDir>
#include <QProcess> #include <QProcess>
#include <QtGlobal> #include <QtGlobal>
#include <stdexcept>
static const char kCoreBinary[] = "syntool"; static const char kCoreBinary[] = "syntool";
@ -34,16 +32,14 @@ static const char kSerialKeyFilename[] = "Synergy.subkey";
static const char kSerialKeyFilename[] = ".synergy.subkey"; static const char kSerialKeyFilename[] = ".synergy.subkey";
#endif #endif
CoreInterface::CoreInterface() {}
QString CoreInterface::getProfileDir() { QString CoreInterface::getProfileDir() {
QStringList args("--get-profile-dir"); QStringList args("--get-profile-dir");
return run(args); return QDir::cleanPath(run(args));
} }
QString CoreInterface::getInstalledDir() { QString CoreInterface::getInstalledDir() {
QStringList args("--get-installed-dir"); QStringList args("--get-installed-dir");
return run(args); return QDir::cleanPath(run(args));
} }
QString CoreInterface::getArch() { QString CoreInterface::getArch() {
@ -52,11 +48,12 @@ QString CoreInterface::getArch() {
} }
QString CoreInterface::getSerialKeyFilePath() { QString CoreInterface::getSerialKeyFilePath() {
QString filename = getProfileDir() + QDir::separator() + kSerialKeyFilename; auto filename = getProfileDir() + QDir::separator() + kSerialKeyFilename;
return filename; return QDir::cleanPath(filename);
} }
QString CoreInterface::run(const QStringList &args, const QString &input) { QString
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);

View file

@ -21,11 +21,9 @@
class CoreInterface { class CoreInterface {
public: public:
CoreInterface();
QString getProfileDir(); QString getProfileDir();
QString getInstalledDir(); QString getInstalledDir();
QString getArch(); QString getArch();
QString getSerialKeyFilePath(); QString getSerialKeyFilePath();
QString run(const QStringList &args, const QString &input = ""); QString run(const QStringList &args, const QString &input = "") const;
}; };

View file

@ -33,5 +33,3 @@
// ElevateNever | false | false // ElevateNever | false | false
// //
enum ElevateMode { ElevateAsNeeded = 0, ElevateAlways = 1, ElevateNever = 2 }; enum ElevateMode { ElevateAsNeeded = 0, ElevateAlways = 1, ElevateNever = 2 };
extern const ElevateMode kDefaultElevateMode;

View file

@ -16,7 +16,10 @@
*/ */
#include "LicenseManager.h" #include "LicenseManager.h"
#include "ActivationNotifier.h"
#include "AppConfig.h" #include "AppConfig.h"
#include <QDateTime> #include <QDateTime>
#include <QLocale> #include <QLocale>
#include <QThread> #include <QThread>
@ -66,7 +69,7 @@ void checkSerialKey(const SerialKey &serialKey, bool acceptExpired) {
} // namespace } // namespace
LicenseManager::LicenseManager(AppConfig *appConfig) LicenseManager::LicenseManager(AppConfig *appConfig)
: m_AppConfig(appConfig), : m_pAppConfig(appConfig),
m_serialKey(appConfig->edition()), m_serialKey(appConfig->edition()),
m_registry(*appConfig) {} m_registry(*appConfig) {}
@ -76,14 +79,14 @@ void LicenseManager::setSerialKey(SerialKey serialKey, bool acceptExpired) {
if (serialKey != m_serialKey) { if (serialKey != m_serialKey) {
using std::swap; using std::swap;
swap(serialKey, m_serialKey); swap(serialKey, m_serialKey);
m_AppConfig->setSerialKey(QString::fromStdString(m_serialKey.toString())); m_pAppConfig->setSerialKey(QString::fromStdString(m_serialKey.toString()));
emit showLicenseNotice(getLicenseNotice()); emit showLicenseNotice(getLicenseNotice());
validateSerialKey(); validateSerialKey();
m_registry.scheduleRegistration(); m_registry.scheduleRegistration();
if (m_serialKey.edition() != serialKey.edition()) { if (m_serialKey.edition() != serialKey.edition()) {
m_AppConfig->setEdition(m_serialKey.edition()); m_pAppConfig->setEdition(m_serialKey.edition());
emit editionChanged(m_serialKey.edition()); emit editionChanged(m_serialKey.edition());
} }
} }
@ -119,13 +122,14 @@ QString LicenseManager::activeEditionName() const {
const SerialKey &LicenseManager::serialKey() const { return m_serialKey; } const SerialKey &LicenseManager::serialKey() const { return m_serialKey; }
void LicenseManager::refresh() { void LicenseManager::refresh() {
if (!m_AppConfig->serialKey().isEmpty()) { if (!m_pAppConfig->serialKey().isEmpty()) {
try { try {
SerialKey serialKey(m_AppConfig->serialKey().toStdString()); SerialKey serialKey(m_pAppConfig->serialKey().toStdString());
setSerialKey(serialKey, true); setSerialKey(serialKey, true);
} catch (...) { } catch (const std::exception &e) {
qDebug() << e.what();
m_serialKey = SerialKey(); m_serialKey = SerialKey();
m_AppConfig->clearSerialKey(); m_pAppConfig->clearSerialKey();
} }
} }
if (!m_serialKey.isValid()) { if (!m_serialKey.isValid()) {
@ -133,10 +137,6 @@ void LicenseManager::refresh() {
} }
} }
void LicenseManager::skipActivation() const {
notifyActivation("skip:unknown");
}
QString LicenseManager::getEditionName(Edition const edition, bool trial) { QString LicenseManager::getEditionName(Edition const edition, bool trial) {
SerialKeyEdition KeyEdition(edition); SerialKeyEdition KeyEdition(edition);
std::string name = KeyEdition.getProductName(); std::string name = KeyEdition.getProductName();

View file

@ -17,13 +17,11 @@
#pragma once #pragma once
#include <ActivationNotifier.h>
#include <QObject>
#include <shared/EditionType.h>
#include <shared/SerialKey.h>
#include <utility>
#include "LicenseRegistry.h" #include "LicenseRegistry.h"
#include "shared/EditionType.h"
#include "shared/SerialKey.h"
#include <QObject>
class AppConfig; class AppConfig;
@ -37,14 +35,13 @@ public:
Edition activeEdition() const; Edition activeEdition() const;
QString activeEditionName() const; QString activeEditionName() const;
const SerialKey &serialKey() const; const SerialKey &serialKey() const;
void skipActivation() const;
void notifyUpdate(QString fromVersion, QString toVersion) const; void notifyUpdate(QString fromVersion, QString toVersion) const;
static QString getEditionName(Edition edition, bool trial = false); static QString getEditionName(Edition edition, bool trial = false);
void notifyActivation(QString identity) const; void notifyActivation(QString identity) const;
QString getLicenseNotice() const; QString getLicenseNotice() const;
private: private:
AppConfig *m_AppConfig; AppConfig *m_pAppConfig;
SerialKey m_serialKey; SerialKey m_serialKey;
LicenseRegistry m_registry; LicenseRegistry m_registry;

View file

@ -23,6 +23,9 @@
#include "LicenseRegistry.h" #include "LicenseRegistry.h"
#include <QSysInfo> #include <QSysInfo>
const char *const kLicenseRegistryUrl =
"https://api2.prod.symless.com/license/register";
LicenseRegistry::LicenseRegistry(AppConfig &config) : m_config(config) { LicenseRegistry::LicenseRegistry(AppConfig &config) : m_config(config) {
connect(&m_timer, SIGNAL(timeout()), this, SLOT(registerLicense())); connect(&m_timer, SIGNAL(timeout()), this, SLOT(registerLicense()));
} }
@ -30,8 +33,7 @@ LicenseRegistry::LicenseRegistry(AppConfig &config) : m_config(config) {
void LicenseRegistry::registerLicense() { void LicenseRegistry::registerLicense() {
m_timer.stop(); m_timer.stop();
if (m_config.edition() == Edition::kBusiness) { if (m_config.edition() == Edition::kBusiness) {
const auto REGISTER_LICENSE_URL = m_config.getLicenseRegistryUrl(); const auto url = QUrl(kLicenseRegistryUrl);
const auto url = QUrl(REGISTER_LICENSE_URL);
auto request = QNetworkRequest(url); auto request = QNetworkRequest(url);
request.setHeader(QNetworkRequest::ContentTypeHeader, "application/json"); request.setHeader(QNetworkRequest::ContentTypeHeader, "application/json");
@ -73,18 +75,18 @@ QByteArray LicenseRegistry::getRequestData() const {
data["guid"] = guid; data["guid"] = guid;
data["guid_type"] = "system"; data["guid_type"] = "system";
} else { } else {
data["guid"] = m_config.getGuid(); data["guid"] = m_config.guid();
data["guid_type"] = "synergy"; data["guid_type"] = "synergy";
} }
data["key"] = m_config.serialKey(); data["key"] = m_config.serialKey();
data["is_server"] = m_config.getServerGroupChecked(); data["is_server"] = m_config.serverGroupChecked();
return QJsonDocument(data).toJson(); return QJsonDocument(data).toJson();
} }
void LicenseRegistry::scheduleRegistration() { void LicenseRegistry::scheduleRegistration() {
const auto nextCheck = m_config.getLicenseNextCheck(); const auto nextCheck = m_config.licenseNextCheck();
const auto currentTimestamp = static_cast<unsigned long long>(time(nullptr)); const auto currentTimestamp = static_cast<unsigned long long>(time(nullptr));
if (currentTimestamp >= nextCheck) { if (currentTimestamp >= nextCheck) {

View file

@ -16,11 +16,6 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>. * along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
#define DOWNLOAD_URL "http://symless.com/?source=gui"
#define HELP_URL "http://symless.com/help?source=gui"
#include <array>
#include "MainWindow.h" #include "MainWindow.h"
#include "AboutDialog.h" #include "AboutDialog.h"
@ -29,8 +24,8 @@
#include "LicenseManager.h" #include "LicenseManager.h"
#include "ServerConfigDialog.h" #include "ServerConfigDialog.h"
#include "SettingsDialog.h" #include "SettingsDialog.h"
#include <QPushButton> #include "shared/EditionType.h"
#include <shared/EditionType.h> #include <memory>
#if defined(Q_OS_MAC) #if defined(Q_OS_MAC)
#include "OSXHelpers.h" #include "OSXHelpers.h"
@ -42,39 +37,46 @@
#include <QMenuBar> #include <QMenuBar>
#include <QMessageBox> #include <QMessageBox>
#include <QNetworkAccessManager> #include <QNetworkAccessManager>
#include <QPushButton>
#include <QRegularExpression> #include <QRegularExpression>
#include <QtCore> #include <QtCore>
#include <QtGui> #include <QtGui>
#include <QtNetwork> #include <QtNetwork>
#include <array>
#if defined(Q_OS_MAC) #if defined(Q_OS_MAC)
#include <ApplicationServices/ApplicationServices.h> #include <ApplicationServices/ApplicationServices.h>
#endif #endif
static const int debugLogLevel = 1; static const char *const kDownloadUrl = "https://symless.com/?source=gui";
static const char *const kHelpUrl = "https://symless.com/help?source=gui";
static const int kRetryDelay = 1000;
static const int kDebugLogLevel = 1;
static const char *synergyLightIconFiles[] = { #if defined(Q_OS_MAC)
static const char *const kLightIconFiles[] = {
":/res/icons/64x64/synergy-light-disconnected.png", ":/res/icons/64x64/synergy-light-disconnected.png",
":/res/icons/64x64/synergy-light-disconnected.png", ":/res/icons/64x64/synergy-light-disconnected.png",
":/res/icons/64x64/synergy-light-connected.png", ":/res/icons/64x64/synergy-light-connected.png",
":/res/icons/64x64/synergy-light-transfering.png", ":/res/icons/64x64/synergy-light-transfering.png",
":/res/icons/64x64/synergy-light-disconnected.png"}; ":/res/icons/64x64/synergy-light-disconnected.png"};
static const char *synergyDarkIconFiles[] = { static const char *const kDarkIconFiles[] = {
":/res/icons/64x64/synergy-dark-disconnected.png", ":/res/icons/64x64/synergy-dark-disconnected.png",
":/res/icons/64x64/synergy-dark-disconnected.png", ":/res/icons/64x64/synergy-dark-disconnected.png",
":/res/icons/64x64/synergy-dark-connected.png", ":/res/icons/64x64/synergy-dark-connected.png",
":/res/icons/64x64/synergy-dark-transfering.png", ":/res/icons/64x64/synergy-dark-transfering.png",
":/res/icons/64x64/synergy-dark-disconnected.png" // synergyPendingRetry ":/res/icons/64x64/synergy-dark-disconnected.png"};
};
static const char *synergyDefaultIconFiles[] = { #endif
":/res/icons/16x16/synergy-disconnected.png", // synergyDisconnected
":/res/icons/16x16/synergy-disconnected.png", // synergyConnecting static const char *const kDefaultIconFiles[] = {
":/res/icons/16x16/synergy-connected.png", // synergyConnected ":/res/icons/16x16/synergy-disconnected.png",
":/res/icons/16x16/synergy-transfering.png", // synergyListening ":/res/icons/16x16/synergy-disconnected.png",
":/res/icons/16x16/synergy-disconnected.png" // synergyPendingRetry ":/res/icons/16x16/synergy-connected.png",
}; ":/res/icons/16x16/synergy-transfering.png",
":/res/icons/16x16/synergy-disconnected.png"};
#ifdef SYNERGY_ENABLE_LICENSING #ifdef SYNERGY_ENABLE_LICENSING
MainWindow::MainWindow(AppConfig &appConfig, LicenseManager &licenseManager) MainWindow::MainWindow(AppConfig &appConfig, LicenseManager &licenseManager)
@ -84,21 +86,9 @@ MainWindow::MainWindow(AppConfig &appConfig)
: :
#ifdef SYNERGY_ENABLE_LICENSING #ifdef SYNERGY_ENABLE_LICENSING
m_LicenseManager(&licenseManager), m_LicenseManager(&licenseManager),
m_ActivationDialogRunning(false),
#endif #endif
m_AppConfig(&appConfig), m_AppConfig(appConfig),
m_pSynergy(NULL), m_ServerConfig(5, 3, &m_AppConfig, this),
m_SynergyState(synergyDisconnected),
m_ServerConfig(5, 3, m_AppConfig, this),
m_AlreadyHidden(false),
m_pMenuBar(NULL),
m_pMenuFile(NULL),
m_pMenuEdit(NULL),
m_pMenuWindow(NULL),
m_pMenuHelp(NULL),
m_pCancelButton(NULL),
m_ExpectedRunningState(kStopped),
m_SecureSocket(false),
m_serverConnection(*this), m_serverConnection(*this),
m_clientConnection(*this) { m_clientConnection(*this) {
@ -109,16 +99,19 @@ MainWindow::MainWindow(AppConfig &appConfig)
m_pRadioGroupClient->setAttribute(Qt::WA_MacShowFocusRect, 0); m_pRadioGroupClient->setAttribute(Qt::WA_MacShowFocusRect, 0);
#endif #endif
m_ServerConfig.loadSettings();
createMenuBar(); createMenuBar();
loadSettings(); loadSettings();
initConnections(); initConnections();
m_pWidgetUpdate->hide(); m_pWidgetUpdate->hide();
m_VersionChecker.setApp(appPath(appConfig.synergycName())); m_VersionChecker.setApp(appPath(appConfig.coreClientName()));
updateScreenName(); updateScreenName();
connect( connect(
m_AppConfig, SIGNAL(screenNameChanged()), this, SLOT(updateScreenName())); appConfigPtr(), SIGNAL(screenNameChanged()), this,
SLOT(updateScreenName()));
m_pLabelIpAddresses->setText( m_pLabelIpAddresses->setText(
tr("This computer's IP addresses: %1").arg(getIPAddresses())); tr("This computer's IP addresses: %1").arg(getIPAddresses()));
@ -172,14 +165,14 @@ MainWindow::MainWindow(AppConfig &appConfig)
#endif #endif
connect( connect(
m_AppConfig, SIGNAL(sslToggled()), this, SLOT(updateLocalFingerprint()), appConfigPtr(), SIGNAL(sslToggled()), this,
Qt::QueuedConnection); SLOT(updateLocalFingerprint()), Qt::QueuedConnection);
updateWindowTitle(); updateWindowTitle();
QString lastVersion = m_AppConfig->lastVersion(); QString lastVersion = m_AppConfig.lastVersion();
if (lastVersion != SYNERGY_VERSION) { if (lastVersion != SYNERGY_VERSION) {
m_AppConfig->setLastVersion(SYNERGY_VERSION); m_AppConfig.setLastVersion(SYNERGY_VERSION);
#ifdef SYNERGY_ENABLE_LICENSING #ifdef SYNERGY_ENABLE_LICENSING
m_LicenseManager->notifyUpdate(lastVersion, SYNERGY_VERSION); m_LicenseManager->notifyUpdate(lastVersion, SYNERGY_VERSION);
@ -193,13 +186,12 @@ MainWindow::MainWindow(AppConfig &appConfig)
MainWindow::~MainWindow() { MainWindow::~MainWindow() {
if (appConfig().processMode() == ProcessMode::kDesktop) { if (appConfig().processMode() == ProcessMode::kDesktop) {
m_ExpectedRunningState = kStopped; m_ExpectedRunningState = RuningState::Stopped;
try { try {
stopDesktop(); stopDesktop();
} catch (...) { } catch (const std::exception &e) {
// do not throw, since throwing from a dtor can result in unreliable qDebug() << e.what();
// behaviour. qFatal("Failed to stop synergy desktop process");
qCritical() << "error stopping desktop in main window destructor";
} }
} }
} }
@ -207,16 +199,15 @@ MainWindow::~MainWindow() {
void MainWindow::open() { void MainWindow::open() {
std::array<QAction *, 7> trayMenu = { std::array<QAction *, 7> trayMenu = {
m_pActionStartSynergy, m_pActionStopSynergy, nullptr, m_pActionStartCore, m_pActionStopCore, nullptr, m_pActionMinimize,
m_pActionMinimize, m_pActionRestore, nullptr, m_pActionRestore, nullptr, m_pActionQuit};
m_pActionQuit};
m_trayIcon.create(trayMenu, [this](QObject const *o, const char *s) { m_trayIcon.create(trayMenu, [this](QObject const *o, const char *s) {
connect(o, s, this, SLOT(trayActivated(QSystemTrayIcon::ActivationReason))); connect(o, s, this, SLOT(trayActivated(QSystemTrayIcon::ActivationReason)));
setIcon(synergyDisconnected); setIcon(CoreState::Disconnected);
}); });
if (appConfig().getAutoHide()) { if (appConfig().autoHide()) {
hide(); hide();
} else { } else {
showNormal(); showNormal();
@ -229,7 +220,7 @@ void MainWindow::open() {
// confuses first time users, who think synergy has crashed). // confuses first time users, who think synergy has crashed).
if (appConfig().startedBefore() && if (appConfig().startedBefore() &&
appConfig().processMode() == ProcessMode::kDesktop) { appConfig().processMode() == ProcessMode::kDesktop) {
startSynergy(); startCore();
} }
} }
@ -251,8 +242,8 @@ void MainWindow::createMenuBar() {
#endif #endif
m_pMenuBar->addAction(m_pMenuHelp->menuAction()); m_pMenuBar->addAction(m_pMenuHelp->menuAction());
m_pMenuFile->addAction(m_pActionStartSynergy); m_pMenuFile->addAction(m_pActionStartCore);
m_pMenuFile->addAction(m_pActionStopSynergy); m_pMenuFile->addAction(m_pActionStopCore);
m_pMenuFile->addSeparator(); m_pMenuFile->addSeparator();
m_pMenuFile->addAction(m_pActivate); m_pMenuFile->addAction(m_pActivate);
m_pMenuFile->addSeparator(); m_pMenuFile->addSeparator();
@ -269,19 +260,18 @@ void MainWindow::createMenuBar() {
} }
void MainWindow::loadSettings() { void MainWindow::loadSettings() {
enableServer(appConfig().getServerGroupChecked()); enableServer(appConfig().serverGroupChecked());
enableClient(appConfig().getClientGroupChecked()); enableClient(appConfig().clientGroupChecked());
m_pLineEditHostname->setText(appConfig().getServerHostname()); m_pLineEditHostname->setText(appConfig().serverHostname());
m_pLineEditClienIp->setText(serverConfig().getClientAddress()); m_pLineEditClienIp->setText(serverConfig().getClientAddress());
} }
void MainWindow::initConnections() { void MainWindow::initConnections() {
connect(m_pActionMinimize, SIGNAL(triggered()), this, SLOT(hide())); connect(m_pActionMinimize, SIGNAL(triggered()), this, SLOT(hide()));
connect(m_pActionRestore, SIGNAL(triggered()), this, SLOT(showNormal())); connect(m_pActionRestore, SIGNAL(triggered()), this, SLOT(showNormal()));
connect( connect(m_pActionStartCore, SIGNAL(triggered()), this, SLOT(actionStart()));
m_pActionStartSynergy, SIGNAL(triggered()), this, SLOT(actionStart())); connect(m_pActionStopCore, SIGNAL(triggered()), this, SLOT(stopCore()));
connect(m_pActionStopSynergy, SIGNAL(triggered()), this, SLOT(stopSynergy()));
connect(m_pActionQuit, SIGNAL(triggered()), qApp, SLOT(quit())); connect(m_pActionQuit, SIGNAL(triggered()), qApp, SLOT(quit()));
connect( connect(
&m_VersionChecker, SIGNAL(updateFound(const QString &)), this, &m_VersionChecker, SIGNAL(updateFound(const QString &)), this,
@ -289,35 +279,34 @@ void MainWindow::initConnections() {
} }
void MainWindow::saveSettings() { void MainWindow::saveSettings() {
// program settings
appConfig().setServerGroupChecked(m_pRadioGroupServer->isChecked()); appConfig().setServerGroupChecked(m_pRadioGroupServer->isChecked());
appConfig().setClientGroupChecked(m_pRadioGroupClient->isChecked()); appConfig().setClientGroupChecked(m_pRadioGroupClient->isChecked());
appConfig().setServerHostname(m_pLineEditHostname->text()); appConfig().setServerHostname(m_pLineEditHostname->text());
serverConfig().setClientAddress(m_pLineEditClienIp->text()); serverConfig().setClientAddress(m_pLineEditClienIp->text());
/* Save everything */ appConfig().config().saveAll();
GUI::Config::ConfigWriter::make()->globalSave();
} }
void MainWindow::setIcon(qSynergyState state) const { void MainWindow::setIcon(CoreState state) const {
QIcon icon; QIcon icon;
auto index = static_cast<int>(state);
#ifdef Q_OS_MAC #ifdef Q_OS_MAC
switch (getOSXIconsTheme()) { switch (getOSXIconsTheme()) {
case IconsTheme::ICONS_DARK: case IconsTheme::ICONS_DARK:
icon.addFile(synergyDarkIconFiles[state]); icon.addFile(kDarkIconFiles[index]);
break; break;
case IconsTheme::ICONS_LIGHT: case IconsTheme::ICONS_LIGHT:
icon.addFile(synergyLightIconFiles[state]); icon.addFile(kLightIconFiles[index]);
break; break;
case IconsTheme::ICONS_TEMPLATE: case IconsTheme::ICONS_TEMPLATE:
default: default:
icon.addFile(synergyDarkIconFiles[state]); icon.addFile(kDarkIconFiles[index]);
icon.setIsMask(true); icon.setIsMask(true);
break; break;
} }
#else #else
icon.addFile(synergyDefaultIconFiles[state]); icon.addFile(kDefaultIconFiles[index]);
#endif #endif
m_trayIcon.set(icon); m_trayIcon.set(icon);
@ -335,8 +324,8 @@ void MainWindow::trayActivated(QSystemTrayIcon::ActivationReason reason) {
} }
void MainWindow::logOutput() { void MainWindow::logOutput() {
if (m_pSynergy) { if (m_pCoreProcess) {
QString text(m_pSynergy->readAllStandardOutput()); QString text(m_pCoreProcess->readAllStandardOutput());
for (QString line : text.split(QRegularExpression("\r|\n|\r\n"))) { for (QString line : text.split(QRegularExpression("\r|\n|\r\n"))) {
if (!line.isEmpty()) { if (!line.isEmpty()) {
appendLogRaw(line); appendLogRaw(line);
@ -346,8 +335,8 @@ void MainWindow::logOutput() {
} }
void MainWindow::logError() { void MainWindow::logError() {
if (m_pSynergy) { if (m_pCoreProcess) {
appendLogRaw(m_pSynergy->readAllStandardError()); appendLogRaw(m_pCoreProcess->readAllStandardError());
} }
} }
@ -357,7 +346,7 @@ void MainWindow::updateFound(const QString &version) {
"Version <b>%1</b> is now available to " "Version <b>%1</b> is now available to "
"<a href=\"%2\">download</a>.</p>") "<a href=\"%2\">download</a>.</p>")
.arg(version) .arg(version)
.arg(DOWNLOAD_URL)); .arg(kDownloadUrl));
} }
void MainWindow::appendLogInfo(const QString &text) { void MainWindow::appendLogInfo(const QString &text) {
@ -365,7 +354,7 @@ void MainWindow::appendLogInfo(const QString &text) {
} }
void MainWindow::appendLogDebug(const QString &text) { void MainWindow::appendLogDebug(const QString &text) {
if (appConfig().logLevel() >= debugLogLevel) { if (appConfig().logLevel() >= kDebugLogLevel) {
appendLogRaw(getTimeStamp() + " DEBUG: " + text); appendLogRaw(getTimeStamp() + " DEBUG: " + text);
} }
} }
@ -395,7 +384,7 @@ void MainWindow::handleIdleService(const QString &text) {
// only start if there is no active service running // only start if there is no active service running
if (!line.isEmpty() && line.contains("service status: idle") && if (!line.isEmpty() && line.contains("service status: idle") &&
appConfig().startedBefore()) { appConfig().startedBefore()) {
startSynergy(); startCore();
} }
} }
} }
@ -428,7 +417,7 @@ void MainWindow::checkConnected(const QString &line) {
} }
if (line.contains("connected to server") || line.contains("has connected")) { if (line.contains("connected to server") || line.contains("has connected")) {
setSynergyState(synergyConnected); setCoreState(CoreState::Connected);
if (!appConfig().startedBefore() && isVisible()) { if (!appConfig().startedBefore() && isVisible()) {
QMessageBox::information( QMessageBox::information(
@ -440,13 +429,13 @@ void MainWindow::checkConnected(const QString &line) {
appConfig().setStartedBefore(true); appConfig().setStartedBefore(true);
} }
} else if (line.contains("started server")) { } else if (line.contains("started server")) {
setSynergyState(synergyListening); setCoreState(CoreState::Listening);
} else if ( } else if (
line.contains("disconnected from server") || line.contains("disconnected from server") ||
line.contains("process exited")) { line.contains("process exited")) {
setSynergyState(synergyDisconnected); setCoreState(CoreState::Disconnected);
} else if (line.contains("connecting to")) { } else if (line.contains("connecting to")) {
setSynergyState(synergyConnecting); setCoreState(CoreState::Connecting);
} }
} }
@ -474,7 +463,7 @@ void MainWindow::checkFingerprint(const QString &line) {
static bool messageBoxAlreadyShown = false; static bool messageBoxAlreadyShown = false;
if (!messageBoxAlreadyShown) { if (!messageBoxAlreadyShown) {
stopSynergy(); stopCore();
messageBoxAlreadyShown = true; messageBoxAlreadyShown = true;
QMessageBox::StandardButton fingerprintReply = QMessageBox::information( QMessageBox::StandardButton fingerprintReply = QMessageBox::information(
@ -493,7 +482,7 @@ void MainWindow::checkFingerprint(const QString &line) {
if (fingerprintReply == QMessageBox::Yes) { if (fingerprintReply == QMessageBox::Yes) {
// restart core process after trusting fingerprint. // restart core process after trusting fingerprint.
Fingerprint::trustedServers().trust(fingerprint); Fingerprint::trustedServers().trust(fingerprint);
startSynergy(); startCore();
} }
messageBoxAlreadyShown = false; messageBoxAlreadyShown = false;
@ -536,9 +525,9 @@ QString MainWindow::getTimeStamp() {
return '[' + current.toString(Qt::ISODate) + ']'; return '[' + current.toString(Qt::ISODate) + ']';
} }
void MainWindow::restartSynergy() { void MainWindow::restartCore() {
stopSynergy(); stopCore();
startSynergy(); startCore();
} }
void MainWindow::showEvent(QShowEvent *event) { void MainWindow::showEvent(QShowEvent *event) {
@ -548,7 +537,7 @@ void MainWindow::showEvent(QShowEvent *event) {
void MainWindow::clearLog() { m_pLogOutput->clear(); } void MainWindow::clearLog() { m_pLogOutput->clear(); }
void MainWindow::startSynergy() { void MainWindow::startCore() {
saveSettings(); saveSettings();
#ifdef Q_OS_MAC #ifdef Q_OS_MAC
@ -564,12 +553,10 @@ void MainWindow::startSynergy() {
} }
m_LicenseManager->registerLicense(); m_LicenseManager->registerLicense();
#endif #endif
bool desktopMode = appConfig().processMode() == ProcessMode::kDesktop;
bool serviceMode = appConfig().processMode() == ProcessMode::kService;
appendLogDebug("starting process"); appendLogDebug("starting process");
m_ExpectedRunningState = kStarted; m_ExpectedRunningState = RuningState::Started;
setSynergyState(synergyConnecting); setCoreState(CoreState::Connecting);
QString app; QString app;
QStringList args; QStringList args;
@ -580,8 +567,10 @@ void MainWindow::startSynergy() {
args << "--name" << appConfig().screenName(); args << "--name" << appConfig().screenName();
if (desktopMode) { ProcessMode mode = appConfig().processMode();
setSynergyProcess(new QProcess(this));
if (mode == ProcessMode::kDesktop) {
m_pCoreProcess = std::make_unique<QProcess>(this);
} else { } else {
// tell client/server to talk to daemon through ipc. // tell client/server to talk to daemon through ipc.
args << "--ipc"; args << "--ipc";
@ -611,25 +600,30 @@ void MainWindow::startSynergy() {
#endif #endif
#if defined(Q_OS_WIN) #if defined(Q_OS_WIN)
if (m_AppConfig->getCryptoEnabled()) { if (m_AppConfig.cryptoEnabled()) {
args << "--enable-crypto"; args << "--enable-crypto";
args << "--tls-cert" args << "--tls-cert" << m_AppConfig.tlsCertPath();
<< QString("\"%1\"").arg(m_AppConfig->getTLSCertPath());
} }
try {
// on windows, the profile directory changes depending on the user that // on windows, the profile directory changes depending on the user that
// launched the process (e.g. when launched with elevation). setting the // launched the process (e.g. when launched with elevation). setting the
// profile dir on launch ensures it uses the same profile dir is used // profile dir on launch ensures it uses the same profile dir is used
// no matter how its relaunched. // no matter how its relaunched.
args << "--profile-dir" << getProfileRootForArg(); args << "--profile-dir" << getProfileRootForArg();
} catch (const std::exception &e) {
qDebug() << e.what();
qFatal("Failed to get profile dir, skipping arg");
}
#else #else
if (m_AppConfig->getCryptoEnabled()) { if (m_AppConfig.cryptoEnabled()) {
args << "--enable-crypto"; args << "--enable-crypto";
args << "--tls-cert" << m_AppConfig->getTLSCertPath(); args << "--tls-cert" << m_AppConfig.tlsCertPath();
} }
#endif #endif
if (m_AppConfig->getPreventSleep()) { if (m_AppConfig.preventSleep()) {
args << "--prevent-sleep"; args << "--prevent-sleep";
} }
@ -639,28 +633,26 @@ void MainWindow::startSynergy() {
appendLogInfo( appendLogInfo(
"starting " + "starting " +
QString(synergyType() == synergyServer ? "server" : "client")); QString(coreMode() == CoreMode::Server ? "server" : "client"));
if ((synergyType() == synergyClient && !clientArgs(args, app)) || if ((coreMode() == CoreMode::Client && !clientArgs(args, app)) ||
(synergyType() == synergyServer && !serverArgs(args, app))) { (coreMode() == CoreMode::Server && !serverArgs(args, app))) {
stopSynergy(); stopCore();
return; return;
} }
if (desktopMode) { if (mode == ProcessMode::kDesktop) {
connect( connect(
synergyProcess(), SIGNAL(finished(int, QProcess::ExitStatus)), this, m_pCoreProcess.get(), SIGNAL(finished(int, QProcess::ExitStatus)), this,
SLOT(synergyFinished(int, QProcess::ExitStatus))); SLOT(coreProcessExit(int, QProcess::ExitStatus)));
connect( connect(
synergyProcess(), SIGNAL(readyReadStandardOutput()), this, m_pCoreProcess.get(), SIGNAL(readyReadStandardOutput()), this,
SLOT(logOutput())); SLOT(logOutput()));
connect( connect(
synergyProcess(), SIGNAL(readyReadStandardError()), this, m_pCoreProcess.get(), SIGNAL(readyReadStandardError()), this,
SLOT(logError())); SLOT(logError()));
} }
qDebug() << args;
// show command if debug log level... // show command if debug log level...
if (appConfig().logLevel() >= 4) { if (appConfig().logLevel() >= 4) {
appendLogInfo(QString("command: %1 %2").arg(app, args.join(" "))); appendLogInfo(QString("command: %1 %2").arg(app, args.join(" ")));
@ -671,9 +663,9 @@ void MainWindow::startSynergy() {
if (appConfig().logToFile()) if (appConfig().logToFile())
appendLogInfo("log file: " + appConfig().logFilename()); appendLogInfo("log file: " + appConfig().logFilename());
if (desktopMode) { if (mode == ProcessMode::kDesktop) {
synergyProcess()->start(app, args); m_pCoreProcess->start(app, args);
if (!synergyProcess()->waitForStarted()) { if (!m_pCoreProcess->waitForStarted()) {
show(); show();
QMessageBox::warning( QMessageBox::warning(
this, tr("Program can not be started"), this, tr("Program can not be started"),
@ -684,9 +676,7 @@ void MainWindow::startSynergy() {
.arg(app))); .arg(app)));
return; return;
} }
} } else if (mode == ProcessMode::kService) {
if (serviceMode) {
QString command(app + " " + args.join(" ")); QString command(app + " " + args.join(" "));
m_IpcClient.sendCommand(command, appConfig().elevateMode()); m_IpcClient.sendCommand(command, appConfig().elevateMode());
} }
@ -694,19 +684,19 @@ void MainWindow::startSynergy() {
void MainWindow::actionStart() { void MainWindow::actionStart() {
m_clientConnection.setCheckConnection(true); m_clientConnection.setCheckConnection(true);
startSynergy(); startCore();
} }
void MainWindow::retryStart() { void MainWindow::retryStart() {
// This function is only called after a failed start // This function is only called after a failed start
// Only start synergy if the current state is pending retry // Only start synergy if the current state is pending retry
if (m_SynergyState == synergyPendingRetry) { if (m_CoreState == CoreState::PendingRetry) {
startSynergy(); startCore();
} }
} }
bool MainWindow::clientArgs(QStringList &args, QString &app) { bool MainWindow::clientArgs(QStringList &args, QString &app) {
app = appPath(appConfig().synergycName()); app = appPath(appConfig().coreClientName());
if (!QFile::exists(app)) { if (!QFile::exists(app)) {
show(); show();
@ -716,38 +706,31 @@ bool MainWindow::clientArgs(QStringList &args, QString &app) {
return false; return false;
} }
#if defined(Q_OS_WIN)
// wrap in quotes so a malicious user can't start \Program.exe as admin.
app = QString("\"%1\"").arg(app);
#endif
if (appConfig().logToFile()) { if (appConfig().logToFile()) {
appConfig().persistLogDir(); appConfig().persistLogDir();
args << "--log" << appConfig().logFilenameCmd(); args << "--log" << appConfig().logFilename();
} }
if (appConfig().getLanguageSync()) { if (appConfig().languageSync()) {
args << "--sync-language"; args << "--sync-language";
} }
if (appConfig().getInvertScrollDirection()) { if (appConfig().invertScrollDirection()) {
args << "--invert-scroll"; args << "--invert-scroll";
} }
if (m_pLineEditHostname->text().isEmpty() && if (appConfig().clientHostMode()) {
!appConfig().getClientHostMode()) {
show();
QMessageBox::warning(
this, tr("Hostname is empty"),
tr("Please fill in a hostname for the synergy "
"client to connect to."));
return false;
}
if (appConfig().getClientHostMode()) {
args << "--host"; args << "--host";
args << ":" + QString::number(appConfig().port()); args << ":" + QString::number(appConfig().port());
} else { } else {
if (m_pLineEditHostname->text().isEmpty()) {
show();
QMessageBox::warning(
this, tr("IP/hostname is empty"),
tr("Please enter a server hostname or IP address."));
return false;
}
QString hostName = m_pLineEditHostname->text(); QString hostName = m_pLineEditHostname->text();
// if interface is IPv6 - ensure that ip is in square brackets // if interface is IPv6 - ensure that ip is in square brackets
if (hostName.count(':') > 1) { if (hostName.count(':') > 1) {
@ -766,8 +749,8 @@ bool MainWindow::clientArgs(QStringList &args, QString &app) {
QString MainWindow::configFilename() { QString MainWindow::configFilename() {
QString configFullPath; QString configFullPath;
if (appConfig().getUseExternalConfig()) { if (appConfig().useExternalConfig()) {
configFullPath = appConfig().getConfigFile(); configFullPath = appConfig().configFile();
} else { } else {
QStringList errors; QStringList errors;
for (auto path : for (auto path :
@ -818,11 +801,12 @@ QString MainWindow::address() const {
} }
QString MainWindow::appPath(const QString &name) { QString MainWindow::appPath(const QString &name) {
return appConfig().synergyProgramDir() + name; QDir dir(QCoreApplication::applicationDirPath());
return dir.filePath(name);
} }
bool MainWindow::serverArgs(QStringList &args, QString &app) { bool MainWindow::serverArgs(QStringList &args, QString &app) {
app = appPath(appConfig().synergysName()); app = appPath(appConfig().coreServerName());
if (!QFile::exists(app)) { if (!QFile::exists(app)) {
QMessageBox::warning( QMessageBox::warning(
@ -831,33 +815,24 @@ bool MainWindow::serverArgs(QStringList &args, QString &app) {
return false; return false;
} }
if (appConfig().getServerClientMode() && if (appConfig().serverClientMode() && m_pLineEditClienIp->text().isEmpty()) {
m_pLineEditClienIp->text().isEmpty()) {
QMessageBox::warning( QMessageBox::warning(
this, tr("Client IP address or name is empty"), this, tr("Client IP address or name is empty"),
tr("Please fill in a client IP address or name.")); tr("Please fill in a client IP address or name."));
return false; return false;
} }
#if defined(Q_OS_WIN)
// wrap in quotes so a malicious user can't start \Program.exe as admin.
app = QString("\"%1\"").arg(app);
#endif
if (appConfig().logToFile()) { if (appConfig().logToFile()) {
appConfig().persistLogDir(); appConfig().persistLogDir();
args << "--log" << appConfig().logFilenameCmd(); args << "--log" << appConfig().logFilename();
} }
QString configFilename = this->configFilename(); QString configFilename = this->configFilename();
if (configFilename.isEmpty()) { if (configFilename.isEmpty()) {
return false; return false;
} }
#if defined(Q_OS_WIN)
// wrap in quotes in case username contains spaces.
configFilename = QString("\"%1\"").arg(configFilename);
#endif
args << "-c" << configFilename << "--address" << address(); args << "-c" << configFilename << "--address" << address();
appendLogInfo("config file: " + configFilename); appendLogInfo("config file: " + configFilename);
@ -870,10 +845,10 @@ bool MainWindow::serverArgs(QStringList &args, QString &app) {
return true; return true;
} }
void MainWindow::stopSynergy() { void MainWindow::stopCore() {
appendLogDebug("stopping process"); appendLogDebug("stopping process");
m_ExpectedRunningState = kStopped; m_ExpectedRunningState = RuningState::Stopped;
if (appConfig().processMode() == ProcessMode::kService) { if (appConfig().processMode() == ProcessMode::kService) {
stopService(); stopService();
@ -881,7 +856,7 @@ void MainWindow::stopSynergy() {
stopDesktop(); stopDesktop();
} }
setSynergyState(synergyDisconnected); setCoreState(CoreState::Disconnected);
// reset so that new connects cause auto-hide. // reset so that new connects cause auto-hide.
m_AlreadyHidden = false; m_AlreadyHidden = false;
@ -894,87 +869,92 @@ void MainWindow::stopService() {
void MainWindow::stopDesktop() { void MainWindow::stopDesktop() {
QMutexLocker locker(&m_StopDesktopMutex); QMutexLocker locker(&m_StopDesktopMutex);
if (!synergyProcess()) { if (!m_pCoreProcess) {
return; return;
} }
appendLogInfo("stopping synergy desktop process"); appendLogInfo("stopping synergy desktop process");
if (synergyProcess()->isOpen()) { if (m_pCoreProcess->isOpen()) {
synergyProcess()->close(); m_pCoreProcess->close();
} }
delete synergyProcess(); m_pCoreProcess->reset();
setSynergyProcess(NULL);
} }
void MainWindow::synergyFinished(int exitCode, QProcess::ExitStatus) { void MainWindow::coreProcessExit(int exitCode, QProcess::ExitStatus) {
if (exitCode == 0) { if (exitCode == 0) {
appendLogInfo(QString("process exited normally")); appendLogInfo("process exited normally");
} else { } else {
appendLogError(QString("process exited with error code: %1").arg(exitCode)); appendLogError(QString("process exited with error code: %1").arg(exitCode));
} }
if (m_ExpectedRunningState == kStarted) { if (m_ExpectedRunningState == RuningState::Started) {
setSynergyState(synergyPendingRetry); if (coreState() != CoreState::PendingRetry) {
QTimer::singleShot(1000, this, SLOT(retryStart())); QTimer::singleShot(kRetryDelay, this, SLOT(retryStart()));
appendLogInfo(QString("detected process not running, auto restarting")); appendLogInfo("detected process not running, auto restarting");
} else { } else {
setSynergyState(synergyDisconnected); appendLogInfo("detected process not running, already auto restarting");
}
setCoreState(CoreState::PendingRetry);
} else {
setCoreState(CoreState::Disconnected);
} }
} }
void MainWindow::setSynergyState(qSynergyState state) { void MainWindow::setCoreState(CoreState state) {
// always assume connection is not secure when connection changes // always assume connection is not secure when connection changes
// to anything except connected. the only way the padlock shows is // to anything except connected. the only way the padlock shows is
// when the correct TLS version string is detected. // when the correct TLS version string is detected.
if (state != synergyConnected) { if (state != CoreState::Connected) {
secureSocket(false); secureSocket(false);
} }
if (synergyState() == state) if (coreState() == state)
return; return;
if ((state == synergyConnected) || (state == synergyConnecting) || if ((state == CoreState::Connected) || (state == CoreState::Connecting) ||
(state == synergyListening) || (state == synergyPendingRetry)) { (state == CoreState::Listening) || (state == CoreState::PendingRetry)) {
disconnect( disconnect(
m_pButtonToggleStart, SIGNAL(clicked()), m_pActionStartSynergy, m_pButtonToggleStart, SIGNAL(clicked()), m_pActionStartCore,
SLOT(trigger())); SLOT(trigger()));
connect( connect(
m_pButtonToggleStart, SIGNAL(clicked()), m_pActionStopSynergy, m_pButtonToggleStart, SIGNAL(clicked()), m_pActionStopCore,
SLOT(trigger())); SLOT(trigger()));
m_pButtonToggleStart->setText(tr("&Stop")); m_pButtonToggleStart->setText(tr("&Stop"));
m_pButtonApply->setEnabled(true); m_pButtonApply->setEnabled(true);
} else if (state == synergyDisconnected) {
m_pActionStartCore->setEnabled(false);
m_pActionStopCore->setEnabled(true);
} else if (state == CoreState::Disconnected) {
disconnect( disconnect(
m_pButtonToggleStart, SIGNAL(clicked()), m_pActionStopSynergy, m_pButtonToggleStart, SIGNAL(clicked()), m_pActionStopCore,
SLOT(trigger())); SLOT(trigger()));
connect( connect(
m_pButtonToggleStart, SIGNAL(clicked()), m_pActionStartSynergy, m_pButtonToggleStart, SIGNAL(clicked()), m_pActionStartCore,
SLOT(trigger())); SLOT(trigger()));
m_pButtonToggleStart->setText(tr("&Start")); m_pButtonToggleStart->setText(tr("&Start"));
m_pButtonApply->setEnabled(false); m_pButtonApply->setEnabled(false);
}
bool running = false; m_pActionStartCore->setEnabled(true);
if (state == synergyConnected || state == synergyListening) { m_pActionStopCore->setEnabled(false);
running = true;
} }
m_pActionStartSynergy->setEnabled(!running);
m_pActionStopSynergy->setEnabled(running);
switch (state) { switch (state) {
case synergyListening: { case CoreState::Listening: {
if (synergyType() == synergyServer) { if (coreMode() == CoreMode::Server) {
setStatus( setStatus(
tr("Synergy is waiting for clients").arg(m_SecureSocketVersion)); tr("Synergy is waiting for clients").arg(m_SecureSocketVersion));
} }
break; break;
} }
case synergyConnected: { case CoreState::Connected: {
if (m_SecureSocket) { if (m_SecureSocket) {
setStatus( setStatus(
tr("Synergy is connected (with %1)").arg(m_SecureSocketVersion)); tr("Synergy is connected (with %1)").arg(m_SecureSocketVersion));
@ -984,20 +964,20 @@ void MainWindow::setSynergyState(qSynergyState state) {
} }
break; break;
} }
case synergyConnecting: case CoreState::Connecting:
setStatus(tr("Synergy is starting...")); setStatus(tr("Synergy is starting..."));
break; break;
case synergyPendingRetry: case CoreState::PendingRetry:
setStatus(tr("There was an error, retrying...")); setStatus(tr("There was an error, retrying..."));
break; break;
case synergyDisconnected: case CoreState::Disconnected:
setStatus(tr("Synergy is not running")); setStatus(tr("Synergy is not running"));
break; break;
} }
setIcon(state); setIcon(state);
m_SynergyState = state; m_CoreState = state;
} }
void MainWindow::setVisible(bool visible) { void MainWindow::setVisible(bool visible) {
@ -1057,8 +1037,8 @@ void MainWindow::setEdition(Edition edition) {
#ifdef SYNERGY_ENABLE_LICENSING #ifdef SYNERGY_ENABLE_LICENSING
void MainWindow::InvalidLicense() { void MainWindow::InvalidLicense() {
stopSynergy(); stopCore();
m_AppConfig->activationHasRun(false); m_AppConfig.activationHasRun(false);
} }
void MainWindow::showLicenseNotice(const QString &notice) { void MainWindow::showLicenseNotice(const QString &notice) {
@ -1074,7 +1054,15 @@ void MainWindow::showLicenseNotice(const QString &notice) {
#endif #endif
void MainWindow::updateLocalFingerprint() { void MainWindow::updateLocalFingerprint() {
if (m_AppConfig->getCryptoEnabled() && Fingerprint::local().fileExists() && bool fingerprintExists = false;
try {
fingerprintExists = Fingerprint::local().fileExists();
} catch (const std::exception &e) {
qDebug() << e.what();
qFatal("Failed to check if fingerprint exists");
}
if (m_AppConfig.cryptoEnabled() && fingerprintExists &&
m_pRadioGroupServer->isChecked()) { m_pRadioGroupServer->isChecked()) {
m_pLabelFingerprint->setVisible(true); m_pLabelFingerprint->setVisible(true);
} else { } else {
@ -1105,7 +1093,7 @@ void MainWindow::on_m_pActionAbout_triggered() {
} }
void MainWindow::on_m_pActionHelp_triggered() { void MainWindow::on_m_pActionHelp_triggered() {
QDesktopServices::openUrl(QUrl(HELP_URL)); QDesktopServices::openUrl(QUrl(kHelpUrl));
} }
void MainWindow::updateWindowTitle() { void MainWindow::updateWindowTitle() {
@ -1123,12 +1111,12 @@ void MainWindow::updateWindowTitle() {
void MainWindow::on_m_pActionSettings_triggered() { void MainWindow::on_m_pActionSettings_triggered() {
auto result = SettingsDialog(this, appConfig()).exec(); auto result = SettingsDialog(this, appConfig()).exec();
if (result == QDialog::Accepted) { if (result == QDialog::Accepted) {
enableServer(appConfig().getServerGroupChecked()); enableServer(appConfig().serverGroupChecked());
enableClient(appConfig().getClientGroupChecked()); enableClient(appConfig().clientGroupChecked());
auto state = synergyState(); auto state = coreState();
if ((state == synergyConnected) || (state == synergyConnecting) || if ((state == CoreState::Connected) || (state == CoreState::Connecting) ||
(state == synergyListening)) { (state == CoreState::Listening)) {
restartSynergy(); restartCore();
} }
} }
} }
@ -1168,10 +1156,10 @@ void MainWindow::showConfigureServer(const QString &message) {
auto result = dlg.exec(); auto result = dlg.exec();
if (result == QDialog::Accepted) { if (result == QDialog::Accepted) {
auto state = synergyState(); auto state = coreState();
if ((state == synergyConnected) || (state == synergyConnecting) || if ((state == CoreState::Connected) || (state == CoreState::Connecting) ||
(state == synergyListening)) { (state == CoreState::Listening)) {
restartSynergy(); restartCore();
} }
} }
} }
@ -1188,7 +1176,7 @@ void MainWindow::on_m_pActivate_triggered() {
void MainWindow::on_m_pButtonApply_clicked() { void MainWindow::on_m_pButtonApply_clicked() {
m_clientConnection.setCheckConnection(true); m_clientConnection.setCheckConnection(true);
restartSynergy(); restartCore();
} }
#ifdef SYNERGY_ENABLE_LICENSING #ifdef SYNERGY_ENABLE_LICENSING
@ -1214,7 +1202,7 @@ int MainWindow::raiseActivationDialog() {
void MainWindow::on_windowShown() { void MainWindow::on_windowShown() {
#ifdef SYNERGY_ENABLE_LICENSING #ifdef SYNERGY_ENABLE_LICENSING
auto serialKey = m_LicenseManager->serialKey(); auto serialKey = m_LicenseManager->serialKey();
if (!m_AppConfig->activationHasRun() && !serialKey.isValid()) { if (!m_AppConfig.activationHasRun() && !serialKey.isValid()) {
setEdition(Edition::kUnregistered); setEdition(Edition::kUnregistered);
raiseActivationDialog(); raiseActivationDialog();
} }
@ -1232,7 +1220,7 @@ QString MainWindow::getProfileRootForArg() {
dir.replace("/.synergy", ""); dir.replace("/.synergy", "");
#endif #endif
return QString("\"%1\"").arg(dir); return dir;
} }
void MainWindow::secureSocket(bool secureSocket) { void MainWindow::secureSocket(bool secureSocket) {
@ -1254,7 +1242,7 @@ void MainWindow::on_m_pLabelFingerprint_linkActivated(const QString &) {
} }
void MainWindow::windowStateChanged() { void MainWindow::windowStateChanged() {
if (windowState() == Qt::WindowMinimized && appConfig().getMinimizeToTray()) if (windowState() == Qt::WindowMinimized && appConfig().minimizeToTray())
hide(); hide();
} }
@ -1267,11 +1255,11 @@ void MainWindow::updateScreenName() {
} }
void MainWindow::enableServer(bool enable) { void MainWindow::enableServer(bool enable) {
m_AppConfig->setServerGroupChecked(enable); m_AppConfig.setServerGroupChecked(enable);
m_pRadioGroupServer->setChecked(enable); m_pRadioGroupServer->setChecked(enable);
if (enable) { if (enable) {
if (m_AppConfig->getServerClientMode()) { if (m_AppConfig.serverClientMode()) {
m_pLabelClientIp->show(); m_pLabelClientIp->show();
m_pLineEditClienIp->show(); m_pLineEditClienIp->show();
m_pButtonConnectToClient->show(); m_pButtonConnectToClient->show();
@ -1296,11 +1284,11 @@ void MainWindow::enableServer(bool enable) {
} }
void MainWindow::enableClient(bool enable) { void MainWindow::enableClient(bool enable) {
m_AppConfig->setClientGroupChecked(enable); m_AppConfig.setClientGroupChecked(enable);
m_pRadioGroupClient->setChecked(enable); m_pRadioGroupClient->setChecked(enable);
if (enable) { if (enable) {
if (m_AppConfig->getClientHostMode()) { if (m_AppConfig.clientHostMode()) {
m_pLabelServerName->hide(); m_pLabelServerName->hide();
m_pLineEditHostname->hide(); m_pLineEditHostname->hide();
m_pButtonConnect->hide(); m_pButtonConnect->hide();
@ -1318,23 +1306,16 @@ void MainWindow::enableClient(bool enable) {
} }
} }
void MainWindow::closeEvent(QCloseEvent *event) {
#if defined(Q_OS_LINUX)
QCoreApplication::quit();
#endif
QWidget::closeEvent(event);
}
void MainWindow::on_m_pRadioGroupServer_clicked(bool) { void MainWindow::on_m_pRadioGroupServer_clicked(bool) {
enableServer(true); enableServer(true);
enableClient(false); enableClient(false);
m_AppConfig->saveSettings(); m_AppConfig.saveSettings();
} }
void MainWindow::on_m_pRadioGroupClient_clicked(bool) { void MainWindow::on_m_pRadioGroupClient_clicked(bool) {
enableClient(true); enableClient(true);
enableServer(false); enableServer(false);
m_AppConfig->saveSettings(); m_AppConfig.saveSettings();
} }
void MainWindow::on_m_pButtonConnect_clicked() { on_m_pButtonApply_clicked(); } void MainWindow::on_m_pButtonConnect_clicked() { on_m_pButtonApply_clicked(); }

View file

@ -24,13 +24,14 @@
#include <QSettings> #include <QSettings>
#include <QSystemTrayIcon> #include <QSystemTrayIcon>
#include <QThread> #include <QThread>
#include <memory>
#include "ui_MainWindowBase.h" #include "ui_MainWindowBase.h"
#include "ActivationDialog.h" #include "ActivationDialog.h"
#include "AppConfig.h" #include "AppConfig.h"
#include "ClientConnection.h" #include "ClientConnection.h"
#include "ConfigWriter.h" #include "Config.h"
#include "QIpcClient.h" #include "QIpcClient.h"
#include "ServerConfig.h" #include "ServerConfig.h"
#include "ServerConnection.h" #include "ServerConnection.h"
@ -70,34 +71,35 @@ class MainWindow : public QMainWindow, public Ui::MainWindowBase {
friend class ClientConnection; friend class ClientConnection;
public: public:
enum qSynergyState { enum class CoreState {
synergyDisconnected, Disconnected,
synergyConnecting, Connecting,
synergyConnected, Connected,
synergyListening, Listening,
synergyPendingRetry PendingRetry
}; };
enum qSynergyType { synergyClient, synergyServer }; enum class CoreMode { Client, Server };
enum qLevel { Error, Info }; enum class LogLevel { Error, Info };
enum qRuningState { kStarted, kStopped }; enum class RuningState { Started, Stopped };
public: public:
#ifdef SYNERGY_ENABLE_LICENSING #ifdef SYNERGY_ENABLE_LICENSING
MainWindow(AppConfig &appConfig, LicenseManager &licenseManager); MainWindow(AppConfig &appConfig, LicenseManager &licenseManager);
#else #else
MainWindow(AppConfig &appConfig); explicit MainWindow(AppConfig &appConfig);
#endif #endif
~MainWindow(); ~MainWindow() override;
public: public:
void setVisible(bool visible); void setVisible(bool visible);
int synergyType() const { CoreMode coreMode() const {
return m_pRadioGroupClient->isChecked() ? synergyClient : synergyServer; auto isClient = m_pRadioGroupClient->isChecked();
return isClient ? CoreMode::Client : CoreMode::Server;
} }
int synergyState() const { return m_SynergyState; } CoreState coreState() const { return m_CoreState; }
QString hostname() const { return m_pLineEditHostname->text(); } QString hostname() const { return m_pLineEditHostname->text(); }
QString configFilename(); QString configFilename();
QString address() const; QString address() const;
@ -124,8 +126,8 @@ public slots:
void appendLogInfo(const QString &text); void appendLogInfo(const QString &text);
void appendLogDebug(const QString &text); void appendLogDebug(const QString &text);
void appendLogError(const QString &text); void appendLogError(const QString &text);
void startSynergy(); void startCore();
void retryStart(); // If the connection failed this will retry a startSynergy void retryStart();
void actionStart(); void actionStart();
void handleIdleService(const QString &text); void handleIdleService(const QString &text);
@ -140,30 +142,26 @@ protected slots:
void on_m_pActionHelp_triggered(); void on_m_pActionHelp_triggered();
void on_m_pActionSettings_triggered(); void on_m_pActionSettings_triggered();
void on_m_pActivate_triggered(); void on_m_pActivate_triggered();
void synergyFinished(int exitCode, QProcess::ExitStatus); void coreProcessExit(int exitCode, QProcess::ExitStatus);
void trayActivated(QSystemTrayIcon::ActivationReason reason); void trayActivated(QSystemTrayIcon::ActivationReason reason);
void stopSynergy(); void stopCore();
void logOutput(); void logOutput();
void logError(); void logError();
void updateFound(const QString &version); void updateFound(const QString &version);
void saveSettings(); void saveSettings();
protected: protected:
// TODO This should be properly using the ConfigWriter system. QSettings &settings() { return *appConfig().config().currentSettings(); }
QSettings &settings() { AppConfig &appConfig() { return m_AppConfig; }
return GUI::Config::ConfigWriter::make()->settings(); AppConfig const &appConfig() const { return m_AppConfig; }
} AppConfig *appConfigPtr() { return &m_AppConfig; }
AppConfig &appConfig() { return *m_AppConfig; }
AppConfig const &appConfig() const { return *m_AppConfig; }
QProcess *synergyProcess() { return m_pSynergy; }
void setSynergyProcess(QProcess *p) { m_pSynergy = p; }
void initConnections(); void initConnections();
void createMenuBar(); void createMenuBar();
void createStatusBar(); void createStatusBar();
void createTrayIcon(); void createTrayIcon();
void loadSettings(); void loadSettings();
void setIcon(qSynergyState state) const; void setIcon(CoreState state) const;
void setSynergyState(qSynergyState state); void setCoreState(CoreState state);
bool checkForApp(int which, QString &app); bool checkForApp(int which, QString &app);
bool clientArgs(QStringList &args, QString &app); bool clientArgs(QStringList &args, QString &app);
bool serverArgs(QStringList &args, QString &app); bool serverArgs(QStringList &args, QString &app);
@ -175,13 +173,6 @@ protected:
void stopDesktop(); void stopDesktop();
void enableServer(bool enable); void enableServer(bool enable);
void enableClient(bool enable); void enableClient(bool enable);
void closeEvent(QCloseEvent *event) override;
#if defined(Q_OS_WIN)
bool isServiceRunning(QString name);
#else
bool isServiceRunning();
#endif
QString getProfileRootForArg(); QString getProfileRootForArg();
void checkConnected(const QString &line); void checkConnected(const QString &line);
@ -194,43 +185,50 @@ protected:
void checkLicense(const QString &line); void checkLicense(const QString &line);
#endif #endif
QString getTimeStamp(); QString getTimeStamp();
void restartSynergy(); void restartCore();
void showEvent(QShowEvent *); void showEvent(QShowEvent *) override;
void secureSocket(bool secureSocket); void secureSocket(bool secureSocket);
void windowStateChanged(); void windowStateChanged();
private: private:
void updateWindowTitle();
#ifdef SYNERGY_ENABLE_LICENSING #ifdef SYNERGY_ENABLE_LICENSING
LicenseManager *m_LicenseManager; LicenseManager *m_LicenseManager = nullptr;
bool m_ActivationDialogRunning;
QStringList m_PendingClientNames;
#endif #endif
AppConfig *m_AppConfig;
QProcess *m_pSynergy; AppConfig &m_AppConfig;
int m_SynergyState;
ServerConfig m_ServerConfig; ServerConfig m_ServerConfig;
bool m_AlreadyHidden;
VersionChecker m_VersionChecker;
QIpcClient m_IpcClient;
QMenuBar *m_pMenuBar;
QMenu *m_pMenuFile;
QMenu *m_pMenuEdit;
QMenu *m_pMenuWindow;
QMenu *m_pMenuHelp;
QAbstractButton *m_pCancelButton;
TrayIcon m_trayIcon;
qRuningState m_ExpectedRunningState;
QMutex m_StopDesktopMutex;
bool m_SecureSocket; // brief Is the program running a secure socket protocol
// (SSL/TLS)
QString m_SecureSocketVersion; // brief Contains the version of the Secure
// Socket currently active
ServerConnection m_serverConnection; ServerConnection m_serverConnection;
ClientConnection m_clientConnection; ClientConnection m_clientConnection;
VersionChecker m_VersionChecker;
QIpcClient m_IpcClient;
TrayIcon m_trayIcon;
QMutex m_StopDesktopMutex;
void updateWindowTitle(); #ifdef SYNERGY_ENABLE_LICENSING
bool m_ActivationDialogRunning = false;
QStringList m_PendingClientNames;
#endif
RuningState m_ExpectedRunningState = RuningState::Stopped;
std::unique_ptr<QProcess> m_pCoreProcess;
QMenuBar *m_pMenuBar = nullptr;
QMenu *m_pMenuFile = nullptr;
QMenu *m_pMenuEdit = nullptr;
QMenu *m_pMenuWindow = nullptr;
QMenu *m_pMenuHelp = nullptr;
QAbstractButton *m_pCancelButton = nullptr;
CoreState m_CoreState = CoreState::Disconnected;
bool m_AlreadyHidden = false;
/// @brief Is the program running a secure socket protocol (SSL/TLS)
bool m_SecureSocket = false;
/// @brief Contains the version of the Secure Socket currently active
QString m_SecureSocketVersion = "";
private slots: private slots:
void on_m_pButtonApply_clicked(); void on_m_pButtonApply_clicked();

View file

@ -6,8 +6,8 @@
<rect> <rect>
<x>0</x> <x>0</x>
<y>0</y> <y>0</y>
<width>720</width> <width>750</width>
<height>552</height> <height>650</height>
</rect> </rect>
</property> </property>
<property name="sizePolicy"> <property name="sizePolicy">
@ -18,8 +18,8 @@
</property> </property>
<property name="minimumSize"> <property name="minimumSize">
<size> <size>
<width>720</width> <width>750</width>
<height>552</height> <height>550</height>
</size> </size>
</property> </property>
<property name="windowTitle"> <property name="windowTitle">
@ -357,7 +357,7 @@ background-color: rgba(192,192,192, 0.1);
</size> </size>
</property> </property>
<property name="text"> <property name="text">
<string>&amp;Configure server</string> <string>&amp;Configure</string>
</property> </property>
</widget> </widget>
</item> </item>
@ -692,7 +692,6 @@ background-color: rgba(192,192,192, 0.1);
<font> <font>
<family>Arial</family> <family>Arial</family>
<pointsize>-1</pointsize> <pointsize>-1</pointsize>
<weight>88</weight>
<bold>true</bold> <bold>true</bold>
</font> </font>
</property> </property>
@ -789,7 +788,7 @@ top: 3px;</string>
<string notr="true">Ctrl+Q</string> <string notr="true">Ctrl+Q</string>
</property> </property>
</action> </action>
<action name="m_pActionStartSynergy"> <action name="m_pActionStartCore">
<property name="text"> <property name="text">
<string>&amp;Start</string> <string>&amp;Start</string>
</property> </property>
@ -797,10 +796,10 @@ top: 3px;</string>
<string>Run</string> <string>Run</string>
</property> </property>
<property name="shortcut"> <property name="shortcut">
<string notr="true">Ctrl+S</string> <string>Ctrl+S</string>
</property> </property>
</action> </action>
<action name="m_pActionStopSynergy"> <action name="m_pActionStopCore">
<property name="enabled"> <property name="enabled">
<bool>false</bool> <bool>false</bool>
</property> </property>
@ -811,7 +810,7 @@ top: 3px;</string>
<string>Stop</string> <string>Stop</string>
</property> </property>
<property name="shortcut"> <property name="shortcut">
<string notr="true">Ctrl+T</string> <string>Ctrl+T</string>
</property> </property>
</action> </action>
<action name="actionShowStatus"> <action name="actionShowStatus">
@ -894,7 +893,7 @@ top: 3px;</string>
<connection> <connection>
<sender>m_pButtonToggleStart</sender> <sender>m_pButtonToggleStart</sender>
<signal>clicked()</signal> <signal>clicked()</signal>
<receiver>m_pActionStartSynergy</receiver> <receiver>m_pActionStartCore</receiver>
<slot>trigger()</slot> <slot>trigger()</slot>
<hints> <hints>
<hint type="sourcelabel"> <hint type="sourcelabel">

View file

@ -1,6 +1,6 @@
/* /*
* synergy -- mouse and keyboard sharing utility * synergy -- mouse and keyboard sharing utility
* Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2012 Symless Ltd.
* Copyright (C) 2008 Volker Lanz (vl@fidra.de) * 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
@ -17,32 +17,31 @@
*/ */
#include "QSynergyApplication.h" #include "QSynergyApplication.h"
#include "MainWindow.h" #include "MainWindow.h"
#include <QtCore> #include <QtCore>
#include <QtGui> #include <QtGui>
QSynergyApplication *QSynergyApplication::s_Instance = NULL;
QSynergyApplication::QSynergyApplication(int &argc, char **argv) QSynergyApplication::QSynergyApplication(int &argc, char **argv)
: QApplication(argc, argv) { : QApplication(argc, argv) {
s_Instance = this;
QFontDatabase::addApplicationFont(":/res/fonts/Arial.ttf"); QFontDatabase::addApplicationFont(":/res/fonts/Arial.ttf");
QFont Arial("Arial"); QFont Arial("Arial");
Arial.setPixelSize(13); Arial.setPixelSize(13);
Arial.setStyleHint(QFont::SansSerif); Arial.setStyleHint(QFont::SansSerif);
setFont(Arial); setFont(Arial);
// Setting the style to 'Fusion' seems to fix issues such as text being
// rendered as black on black. This may not be the style we want long-term
// but it does fix the style issues for now.
setStyle("Fusion");
} }
QSynergyApplication::~QSynergyApplication() {} void QSynergyApplication::commitData(const QSessionManager &) const {
void QSynergyApplication::commitData(QSessionManager &) {
foreach (QWidget *widget, topLevelWidgets()) { foreach (QWidget *widget, topLevelWidgets()) {
MainWindow *mainWindow = qobject_cast<MainWindow *>(widget); MainWindow *mainWindow = qobject_cast<MainWindow *>(widget);
if (mainWindow) if (mainWindow)
mainWindow->saveSettings(); mainWindow->saveSettings();
} }
} }
QSynergyApplication *QSynergyApplication::getInstance() { return s_Instance; }

View file

@ -1,6 +1,6 @@
/* /*
* synergy -- mouse and keyboard sharing utility * synergy -- mouse and keyboard sharing utility
* Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2012 Symless Ltd.
* Copyright (C) 2008 Volker Lanz (vl@fidra.de) * 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
@ -16,9 +16,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(QSYNERGYAPPLICATION__H) #pragma once
#define QSYNERGYAPPLICATION__H
#include <QApplication> #include <QApplication>
@ -27,15 +25,7 @@ class QSessionManager;
class QSynergyApplication : public QApplication { class QSynergyApplication : public QApplication {
public: public:
QSynergyApplication(int &argc, char **argv); QSynergyApplication(int &argc, char **argv);
~QSynergyApplication(); ~QSynergyApplication() override = default;
public: void commitData(const QSessionManager &manager) const;
void commitData(QSessionManager &manager);
static QSynergyApplication *getInstance();
private:
static QSynergyApplication *s_Instance;
}; };
#endif

View file

@ -16,23 +16,20 @@
* 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(SCREEN__H) #pragma once
#define SCREEN__H #include "ScreenConfig.h"
#include <QList> #include <QList>
#include <QPixmap> #include <QPixmap>
#include <QString> #include <QString>
#include <QStringList> #include <QStringList>
#include "BaseConfig.h"
class QSettings; class QSettings;
class QTextStream; class QTextStream;
class ScreenSettingsDialog; class ScreenSettingsDialog;
class Screen : public BaseConfig { class Screen : public ScreenConfig {
friend QDataStream &operator<<(QDataStream &outStream, const Screen &screen); friend QDataStream &operator<<(QDataStream &outStream, const Screen &screen);
friend QDataStream &operator>>(QDataStream &inStream, Screen &screen); friend QDataStream &operator>>(QDataStream &inStream, Screen &screen);
friend class ScreenSettingsDialog; friend class ScreenSettingsDialog;
@ -102,5 +99,3 @@ private:
QDataStream &operator<<(QDataStream &outStream, const Screen &screen); QDataStream &operator<<(QDataStream &outStream, const Screen &screen);
QDataStream &operator>>(QDataStream &inStream, Screen &screen); QDataStream &operator>>(QDataStream &inStream, Screen &screen);
#endif

View file

@ -16,14 +16,14 @@
* 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 "BaseConfig.h" #include "ScreenConfig.h"
const char *BaseConfig::m_ModifierNames[] = {"shift", "ctrl", "alt", const char *ScreenConfig::m_ModifierNames[] = {"shift", "ctrl", "alt",
"meta", "super", "none"}; "meta", "super", "none"};
const char *BaseConfig::m_FixNames[] = { const char *ScreenConfig::m_FixNames[] = {
"halfDuplexCapsLock", "halfDuplexNumLock", "halfDuplexScrollLock", "halfDuplexCapsLock", "halfDuplexNumLock", "halfDuplexScrollLock",
"xtestIsXineramaUnaware"}; "xtestIsXineramaUnaware"};
const char *BaseConfig::m_SwitchCornerNames[] = { const char *ScreenConfig::m_SwitchCornerNames[] = {
"top-left", "top-right", "bottom-left", "bottom-right"}; "top-left", "top-right", "bottom-left", "bottom-right"};

View file

@ -1,6 +1,6 @@
/* /*
* synergy -- mouse and keyboard sharing utility * synergy -- mouse and keyboard sharing utility
* Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2012 Symless Ltd.
* Copyright (C) 2008 Volker Lanz (vl@fidra.de) * 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
@ -16,15 +16,14 @@
* 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(BASECONFIG_H) #pragma once
#define BASECONFIG_H
#include <QSettings> #include <QSettings>
#include <QString> #include <QString>
#include <QVariant> #include <QVariant>
class BaseConfig { /// @brief Screen configuration base class
class ScreenConfig {
public: public:
enum Modifier { enum Modifier {
DefaultMod = -1, DefaultMod = -1,
@ -46,8 +45,8 @@ public:
enum Fix { CapsLock, NumLock, ScrollLock, XTest, NumFixes }; enum Fix { CapsLock, NumLock, ScrollLock, XTest, NumFixes };
protected: protected:
BaseConfig() {} explicit ScreenConfig() = default;
virtual ~BaseConfig() {} ~ScreenConfig() = default;
protected: protected:
template <typename T1, typename T2> template <typename T1, typename T2>
@ -101,5 +100,3 @@ private:
static const char *m_FixNames[]; static const char *m_FixNames[];
static const char *m_SwitchCornerNames[]; static const char *m_SwitchCornerNames[];
}; };
#endif

View file

@ -1,6 +1,6 @@
/* /*
* synergy -- mouse and keyboard sharing utility * synergy -- mouse and keyboard sharing utility
* Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2012 Symless Ltd.
* Copyright (C) 2008 Volker Lanz (vl@fidra.de) * 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
@ -17,7 +17,9 @@
*/ */
#include "ServerConfig.h" #include "ServerConfig.h"
#include "AddClientDialog.h" #include "AddClientDialog.h"
#include "Config.h"
#include "Hotkey.h" #include "Hotkey.h"
#include "MainWindow.h" #include "MainWindow.h"
@ -26,6 +28,8 @@
#include <QPushButton> #include <QPushButton>
#include <QtCore> #include <QtCore>
using synergy::gui::Config;
static const struct { static const struct {
int x; int x;
int y; int y;
@ -42,25 +46,20 @@ const int serverDefaultIndex = 7;
ServerConfig::ServerConfig( ServerConfig::ServerConfig(
int numColumns, int numRows, AppConfig *appConfig, MainWindow *mainWindow) int numColumns, int numRows, AppConfig *appConfig, MainWindow *mainWindow)
: : m_pAppConfig(appConfig),
m_pMainWindow(mainWindow),
m_Screens(numColumns), m_Screens(numColumns),
m_NumColumns(numColumns), m_NumColumns(numColumns),
m_NumRows(numRows), m_NumRows(numRows),
m_pAppConfig(appConfig), m_ClipboardSharingSize(defaultClipboardSharingSize()) {
m_EnableDragAndDrop(false), appConfig->config().registerReceiever(this);
m_DisableLockToScreen(false),
m_ClipboardSharing(true),
m_ClipboardSharingSize(defaultClipboardSharingSize()),
m_pMainWindow(mainWindow) {
GUI::Config::ConfigWriter::make()->registerClass(this);
ServerConfig::loadSettings();
} }
ServerConfig::~ServerConfig() { ServerConfig::~ServerConfig() {
try { try {
ServerConfig::saveSettings(); ServerConfig::saveSettings();
} catch (const std::exception &e) { } catch (const std::exception &e) {
qDebug() << e.what();
m_pMainWindow->appendLogError(e.what()); m_pMainWindow->appendLogError(e.what());
} }
} }
@ -165,8 +164,7 @@ void ServerConfig::saveSettings() {
settings().endGroup(); settings().endGroup();
m_pAppConfig->saveSettings(); m_pAppConfig->saveSettings();
// Tell the config writer there are changes m_pAppConfig->config().markUnsaved();
GUI::Config::ConfigWriter::make()->markUnsaved();
} }
void ServerConfig::loadSettings() { void ServerConfig::loadSettings() {
@ -422,12 +420,12 @@ void ServerConfig::updateServerName() {
} }
} }
const QString &ServerConfig::getConfigFile() const { const QString &ServerConfig::configFile() const {
return m_pAppConfig->getConfigFile(); return m_pAppConfig->configFile();
} }
bool ServerConfig::getUseExternalConfig() const { bool ServerConfig::useExternalConfig() const {
return m_pAppConfig->getUseExternalConfig(); return m_pAppConfig->useExternalConfig();
} }
bool ServerConfig::isFull() const { bool ServerConfig::isFull() const {
@ -543,25 +541,23 @@ size_t ServerConfig::setClipboardSharingSize(size_t size) {
} }
void ServerConfig::setClientAddress(const QString &address) { void ServerConfig::setClientAddress(const QString &address) {
if (m_pAppConfig->getServerClientMode()) { if (m_pAppConfig->serverClientMode()) {
m_clientAddress = address; m_ClientAddress = address;
} }
} }
QString ServerConfig::getClientAddress() const { QString ServerConfig::getClientAddress() const {
QString clientAddress; QString clientAddress;
if (m_pAppConfig->getServerClientMode()) { if (m_pAppConfig->serverClientMode()) {
clientAddress = m_clientAddress.trimmed(); clientAddress = m_ClientAddress.trimmed();
} }
return clientAddress; return clientAddress;
} }
QSettings &ServerConfig::settings() { QSettings &ServerConfig::settings() {
using GUI::Config::ConfigWriter; return *m_pAppConfig->config().currentSettings();
return ConfigWriter::make()->settings();
} }
bool ServerConfig::isHotkeysAvailable() const { bool ServerConfig::isHotkeysAvailable() const {

View file

@ -16,15 +16,13 @@
* 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(SERVERCONFIG__H) #pragma once
#define SERVERCONFIG__H
#include <QList> #include <QList>
#include "BaseConfig.h" #include "CommonConfig.h"
#include "ConfigBase.h"
#include "Hotkey.h" #include "Hotkey.h"
#include "ScreenConfig.h"
#include "ScreenList.h" #include "ScreenList.h"
class QTextStream; class QTextStream;
@ -35,7 +33,7 @@ class ServerConfigDialog;
class MainWindow; class MainWindow;
class AppConfig; class AppConfig;
class ServerConfig : public BaseConfig, public GUI::Config::ConfigBase { class ServerConfig : public ScreenConfig, public synergy::gui::CommonConfig {
friend class ServerConfigDialog; friend class ServerConfigDialog;
friend class ServerConnection; friend class ServerConnection;
friend QTextStream & friend QTextStream &
@ -45,16 +43,10 @@ public:
ServerConfig( ServerConfig(
int numColumns, int numRows, AppConfig *appConfig, int numColumns, int numRows, AppConfig *appConfig,
MainWindow *mainWindow); MainWindow *mainWindow);
~ServerConfig() override;
ServerConfig(const ServerConfig &src) = default;
ServerConfig(ServerConfig &&) = default;
~ServerConfig();
ServerConfig &operator=(const ServerConfig &src) = default;
ServerConfig &operator=(ServerConfig &&) = delete;
bool operator==(const ServerConfig &sc) const; bool operator==(const ServerConfig &sc) const;
public:
const ScreenList &screens() const { return m_Screens; } const ScreenList &screens() const { return m_Screens; }
int numColumns() const { return m_NumColumns; } int numColumns() const { return m_NumColumns; }
int numRows() const { return m_NumRows; } int numRows() const { return m_NumRows; }
@ -84,8 +76,8 @@ public:
int autoAddScreen(const QString name); int autoAddScreen(const QString name);
const QString &getServerName() const; const QString &getServerName() const;
void updateServerName(); void updateServerName();
const QString &getConfigFile() const; const QString &configFile() const;
bool getUseExternalConfig() const; bool useExternalConfig() const;
bool isFull() const; bool isFull() const;
bool isScreenExists(const QString &screenName) const; bool isScreenExists(const QString &screenName) const;
void addClient(const QString &clientName); void addClient(const QString &clientName);
@ -129,27 +121,28 @@ private:
void addToFirstEmptyGrid(const QString &clientName); void addToFirstEmptyGrid(const QString &clientName);
private: private:
bool m_HasHeartbeat = false;
int m_Heartbeat = 0;
bool m_RelativeMouseMoves = false;
bool m_Win32KeepForeground = false;
bool m_HasSwitchDelay = false;
int m_SwitchDelay = 0;
bool m_HasSwitchDoubleTap = false;
int m_SwitchDoubleTap = 0;
int m_SwitchCornerSize = 0;
bool m_EnableDragAndDrop = false;
bool m_DisableLockToScreen = false;
bool m_ClipboardSharing = true;
QString m_ClientAddress = "";
QList<bool> m_SwitchCorners;
HotkeyList m_Hotkeys;
AppConfig *m_pAppConfig;
MainWindow *m_pMainWindow;
ScreenList m_Screens; ScreenList m_Screens;
int m_NumColumns; int m_NumColumns;
int m_NumRows; int m_NumRows;
bool m_HasHeartbeat;
int m_Heartbeat;
bool m_RelativeMouseMoves;
bool m_Win32KeepForeground;
bool m_HasSwitchDelay;
int m_SwitchDelay;
bool m_HasSwitchDoubleTap;
int m_SwitchDoubleTap;
int m_SwitchCornerSize;
QList<bool> m_SwitchCorners;
HotkeyList m_Hotkeys;
AppConfig *m_pAppConfig;
bool m_EnableDragAndDrop;
bool m_DisableLockToScreen;
bool m_ClipboardSharing;
size_t m_ClipboardSharingSize; size_t m_ClipboardSharingSize;
QString m_clientAddress;
MainWindow *m_pMainWindow;
}; };
QTextStream &operator<<(QTextStream &outStream, const ServerConfig &config); QTextStream &operator<<(QTextStream &outStream, const ServerConfig &config);
@ -160,5 +153,3 @@ enum {
kAutoAddScreenManualClient, kAutoAddScreenManualClient,
kAutoAddScreenIgnore kAutoAddScreenIgnore
}; };
#endif

View file

@ -33,9 +33,9 @@ ServerConfigDialog::ServerConfigDialog(
QWidget *parent, ServerConfig &config, AppConfig &appConfig) QWidget *parent, ServerConfig &config, AppConfig &appConfig)
: QDialog(parent, Qt::WindowTitleHint | Qt::WindowSystemMenuHint), : QDialog(parent, Qt::WindowTitleHint | Qt::WindowSystemMenuHint),
Ui::ServerConfigDialogBase(), Ui::ServerConfigDialogBase(),
m_OrigServerConfig(config), m_OriginalServerConfig(config),
m_OrigServerAppConfigUseExternalConfig(config.getUseExternalConfig()), m_OriginalServerConfigIsExternal(config.useExternalConfig()),
m_OrigServerAppConfigExternalConfigFile(config.getConfigFile()), m_OriginalServerConfigUsesExternalFile(config.configFile()),
m_ServerConfig(config), m_ServerConfig(config),
m_ScreenSetupModel( m_ScreenSetupModel(
serverConfig().screens(), serverConfig().numColumns(), serverConfig().screens(), serverConfig().numColumns(),
@ -44,9 +44,8 @@ ServerConfigDialog::ServerConfigDialog(
m_appConfig(appConfig) { m_appConfig(appConfig) {
setupUi(this); setupUi(this);
m_pEditConfigFile->setText(serverConfig().getConfigFile()); m_pEditConfigFile->setText(serverConfig().configFile());
m_pCheckBoxUseExternalConfig->setChecked( m_pCheckBoxUseExternalConfig->setChecked(serverConfig().useExternalConfig());
serverConfig().getUseExternalConfig());
m_pCheckBoxHeartbeat->setChecked(serverConfig().hasHeartbeat()); m_pCheckBoxHeartbeat->setChecked(serverConfig().hasHeartbeat());
m_pSpinBoxHeartbeat->setValue(serverConfig().heartbeat()); m_pSpinBoxHeartbeat->setValue(serverConfig().heartbeat());
@ -62,13 +61,13 @@ ServerConfigDialog::ServerConfigDialog(
m_pSpinBoxSwitchDoubleTap->setValue(serverConfig().switchDoubleTap()); m_pSpinBoxSwitchDoubleTap->setValue(serverConfig().switchDoubleTap());
m_pCheckBoxCornerTopLeft->setChecked( m_pCheckBoxCornerTopLeft->setChecked(
serverConfig().switchCorner(BaseConfig::TopLeft)); serverConfig().switchCorner(ScreenConfig::TopLeft));
m_pCheckBoxCornerTopRight->setChecked( m_pCheckBoxCornerTopRight->setChecked(
serverConfig().switchCorner(BaseConfig::TopRight)); serverConfig().switchCorner(ScreenConfig::TopRight));
m_pCheckBoxCornerBottomLeft->setChecked( m_pCheckBoxCornerBottomLeft->setChecked(
serverConfig().switchCorner(BaseConfig::BottomLeft)); serverConfig().switchCorner(ScreenConfig::BottomLeft));
m_pCheckBoxCornerBottomRight->setChecked( m_pCheckBoxCornerBottomRight->setChecked(
serverConfig().switchCorner(BaseConfig::BottomRight)); serverConfig().switchCorner(ScreenConfig::BottomRight));
m_pSpinBoxSwitchCornerSize->setValue(serverConfig().switchCornerSize()); m_pSpinBoxSwitchCornerSize->setValue(serverConfig().switchCornerSize());
m_pCheckBoxDisableLockToScreen->setChecked( m_pCheckBoxDisableLockToScreen->setChecked(
serverConfig().disableLockToScreen()); serverConfig().disableLockToScreen());
@ -186,25 +185,25 @@ ServerConfigDialog::ServerConfigDialog(
connect( connect(
m_pCheckBoxCornerTopLeft, &QCheckBox::stateChanged, this, m_pCheckBoxCornerTopLeft, &QCheckBox::stateChanged, this,
[this](const int &v) { [this](const int &v) {
serverConfig().setSwitchCorner(BaseConfig::TopLeft, v); serverConfig().setSwitchCorner(ScreenConfig::TopLeft, v);
onChange(); onChange();
}); });
connect( connect(
m_pCheckBoxCornerTopRight, &QCheckBox::stateChanged, this, m_pCheckBoxCornerTopRight, &QCheckBox::stateChanged, this,
[this](const int &v) { [this](const int &v) {
serverConfig().setSwitchCorner(BaseConfig::TopRight, v); serverConfig().setSwitchCorner(ScreenConfig::TopRight, v);
onChange(); onChange();
}); });
connect( connect(
m_pCheckBoxCornerBottomLeft, &QCheckBox::stateChanged, this, m_pCheckBoxCornerBottomLeft, &QCheckBox::stateChanged, this,
[this](const int &v) { [this](const int &v) {
serverConfig().setSwitchCorner(BaseConfig::BottomLeft, v); serverConfig().setSwitchCorner(ScreenConfig::BottomLeft, v);
onChange(); onChange();
}); });
connect( connect(
m_pCheckBoxCornerBottomRight, &QCheckBox::stateChanged, this, m_pCheckBoxCornerBottomRight, &QCheckBox::stateChanged, this,
[this](const int &v) { [this](const int &v) {
serverConfig().setSwitchCorner(BaseConfig::BottomRight, v); serverConfig().setSwitchCorner(ScreenConfig::BottomRight, v);
onChange(); onChange();
}); });
connect( connect(
@ -261,14 +260,14 @@ void ServerConfigDialog::accept() {
// now that the dialog has been accepted, copy the new server config to the // now that the dialog has been accepted, copy the new server config to the
// original one, which is a reference to the one in MainWindow. // original one, which is a reference to the one in MainWindow.
setOrigServerConfig(serverConfig()); setOriginalServerConfig(serverConfig());
QDialog::accept(); QDialog::accept();
} }
void ServerConfigDialog::reject() { void ServerConfigDialog::reject() {
serverConfig().setUseExternalConfig(m_OrigServerAppConfigUseExternalConfig); serverConfig().setUseExternalConfig(m_OriginalServerConfigIsExternal);
serverConfig().setConfigFile(m_OrigServerAppConfigExternalConfigFile); serverConfig().setConfigFile(m_OriginalServerConfigUsesExternalFile);
QDialog::reject(); QDialog::reject();
} }
@ -460,10 +459,9 @@ bool ServerConfigDialog::addComputer(const QString &clientName, bool doSilent) {
void ServerConfigDialog::onChange() { void ServerConfigDialog::onChange() {
bool isAppConfigDataEqual = bool isAppConfigDataEqual =
m_OrigServerAppConfigUseExternalConfig == m_OriginalServerConfigIsExternal == serverConfig().useExternalConfig() &&
serverConfig().getUseExternalConfig() && m_OriginalServerConfigUsesExternalFile == serverConfig().configFile();
m_OrigServerAppConfigExternalConfigFile == serverConfig().getConfigFile();
m_pButtonBox->button(QDialogButtonBox::Ok) m_pButtonBox->button(QDialogButtonBox::Ok)
->setEnabled( ->setEnabled(
!isAppConfigDataEqual || !(m_OrigServerConfig == m_ServerConfig)); !isAppConfigDataEqual || !(m_OriginalServerConfig == m_ServerConfig));
} }

View file

@ -61,15 +61,17 @@ protected slots:
protected: protected:
bool addComputer(const QString &clientName, bool doSilent); bool addComputer(const QString &clientName, bool doSilent);
ServerConfig &serverConfig() { return m_ServerConfig; } ServerConfig &serverConfig() { return m_ServerConfig; }
void setOrigServerConfig(const ServerConfig &s) { m_OrigServerConfig = s; } void setOriginalServerConfig(const ServerConfig &s) {
m_OriginalServerConfig = s;
}
ScreenSetupModel &model() { return m_ScreenSetupModel; } ScreenSetupModel &model() { return m_ScreenSetupModel; }
AppConfig &appConfig() { return m_appConfig; } AppConfig &appConfig() { return m_appConfig; }
private: private:
ServerConfig &m_OrigServerConfig; ServerConfig &m_OriginalServerConfig;
bool m_OrigServerAppConfigUseExternalConfig;
QString m_OrigServerAppConfigExternalConfigFile;
ServerConfig m_ServerConfig; ServerConfig m_ServerConfig;
bool m_OriginalServerConfigIsExternal;
QString m_OriginalServerConfigUsesExternalFile;
ScreenSetupModel m_ScreenSetupModel; ScreenSetupModel m_ScreenSetupModel;
QString m_Message; QString m_Message;
AppConfig &m_appConfig; AppConfig &m_appConfig;

View file

@ -30,7 +30,7 @@ ServerConnection::ServerConnection(MainWindow &parent) : m_parent(parent) {}
void ServerConnection::update(const QString &line) { void ServerConnection::update(const QString &line) {
ServerMessage message(line); ServerMessage message(line);
if (!m_parent.appConfig().getUseExternalConfig() && if (!m_parent.appConfig().useExternalConfig() &&
message.isNewClientMessage() && message.isNewClientMessage() &&
!m_ignoredClients.contains(message.getClientName())) { !m_ignoredClients.contains(message.getClientName())) {
addClient(message.getClientName()); addClient(message.getClientName());
@ -73,6 +73,6 @@ void ServerConnection::configureClient(const QString &clientName) {
&m_parent, m_parent.serverConfig(), m_parent.appConfig()); &m_parent, m_parent.serverConfig(), m_parent.appConfig());
if (dlg.addClient(clientName) && dlg.exec() == QDialog::Accepted) { if (dlg.addClient(clientName) && dlg.exec() == QDialog::Accepted) {
m_parent.restartSynergy(); m_parent.restartCore();
} }
} }

View file

@ -1,6 +1,6 @@
/* /*
* synergy -- mouse and keyboard sharing utility * synergy -- mouse and keyboard sharing utility
* Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2012 Symless Ltd.
* Copyright (C) 2008 Volker Lanz (vl@fidra.de) * 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
@ -15,14 +15,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 "SettingsDialog.h" #include "SettingsDialog.h"
#include "validators/ScreenNameValidator.h"
#include "AppConfig.h" #include "AppConfig.h"
#include "CoreInterface.h"
#include "MainWindow.h" #include "MainWindow.h"
#include "QSynergyApplication.h"
#include "QUtility.h"
#include "SslCertificate.h" #include "SslCertificate.h"
#include "UpgradeDialog.h" #include "UpgradeDialog.h"
@ -31,6 +28,7 @@
#include <QMessageBox> #include <QMessageBox>
#include <QtCore> #include <QtCore>
#include <QtGui> #include <QtGui>
#include <memory>
SettingsDialog::SettingsDialog(QWidget *parent, AppConfig &config) SettingsDialog::SettingsDialog(QWidget *parent, AppConfig &config)
: QDialog(parent, Qt::WindowTitleHint | Qt::WindowSystemMenuHint), : QDialog(parent, Qt::WindowTitleHint | Qt::WindowSystemMenuHint),
@ -43,15 +41,15 @@ SettingsDialog::SettingsDialog(QWidget *parent, AppConfig &config)
loadFromConfig(); loadFromConfig();
m_isSystemAtStart = appConfig().isSystemScoped(); m_isSystemAtStart = appConfig().isSystemScoped();
buttonBox->button(QDialogButtonBox::Save)->setEnabled(false); updateControlsEnabled();
enableControls(appConfig().isWritable());
m_pCheckBoxLanguageSync->setEnabled(isClientMode());
m_pCheckBoxScrollDirection->setEnabled(isClientMode());
const auto &serveConfig = m_pMainWindow->serverConfig(); const auto &serveConfig = m_pMainWindow->serverConfig();
m_pLineEditScreenName->setValidator(new validators::ScreenNameValidator( m_screenNameValidator = std::make_unique<validators::ScreenNameValidator>(
m_pLineEditScreenName, m_pLabelNameError, (&serveConfig.screens()))); m_pLineEditScreenName, nullptr, (&serveConfig.screens()));
connect(
m_screenNameValidator.get(), SIGNAL(finished(QString)), this,
SLOT(on_m_pScreenNameValidator_finished(QString)));
m_pLineEditScreenName->setValidator(m_screenNameValidator.get());
connect( connect(
m_pLineEditLogFilename, SIGNAL(textChanged(QString)), this, m_pLineEditLogFilename, SIGNAL(textChanged(QString)), this,
@ -86,6 +84,11 @@ SettingsDialog::SettingsDialog(QWidget *parent, AppConfig &config)
} }
void SettingsDialog::accept() { void SettingsDialog::accept() {
if (!m_nameError.isEmpty()) {
QMessageBox::warning(this, tr("Invalid screen name"), m_nameError);
return;
}
appConfig().setLoadFromSystemScope(m_pRadioSystemScope->isChecked()); appConfig().setLoadFromSystemScope(m_pRadioSystemScope->isChecked());
appConfig().setScreenName(m_pLineEditScreenName->text()); appConfig().setScreenName(m_pLineEditScreenName->text());
appConfig().setPort(m_pSpinBoxPort->value()); appConfig().setPort(m_pSpinBoxPort->value());
@ -98,13 +101,15 @@ void SettingsDialog::accept() {
appConfig().setAutoHide(m_pCheckBoxAutoHide->isChecked()); appConfig().setAutoHide(m_pCheckBoxAutoHide->isChecked());
appConfig().setPreventSleep(m_pCheckBoxPreventSleep->isChecked()); appConfig().setPreventSleep(m_pCheckBoxPreventSleep->isChecked());
appConfig().setMinimizeToTray(m_pCheckBoxMinimizeToTray->isChecked()); appConfig().setMinimizeToTray(m_pCheckBoxMinimizeToTray->isChecked());
appConfig().setTLSCertPath(m_pLineEditCertificatePath->text()); appConfig().setTlsCertPath(m_pLineEditCertificatePath->text());
appConfig().setTLSKeyLength(m_pComboBoxKeyLength->currentText()); appConfig().setTlsKeyLength(m_pComboBoxKeyLength->currentText());
appConfig().setCryptoEnabled(m_pCheckBoxEnableCrypto->isChecked()); appConfig().setCryptoEnabled(m_pCheckBoxEnableCrypto->isChecked());
appConfig().setLanguageSync(m_pCheckBoxLanguageSync->isChecked()); appConfig().setLanguageSync(m_pCheckBoxLanguageSync->isChecked());
appConfig().setInvertScrollDirection(m_pCheckBoxScrollDirection->isChecked()); appConfig().setInvertScrollDirection(m_pCheckBoxScrollDirection->isChecked());
appConfig().setClientHostMode(m_pCheckBoxClientHostMode->isChecked()); appConfig().setClientHostMode(m_pCheckBoxClientHostMode->isChecked());
appConfig().setServerClientMode(m_pCheckBoxServerClientMode->isChecked()); appConfig().setServerClientMode(m_pCheckBoxServerClientMode->isChecked());
appConfig().setServiceEnabled(m_pCheckBoxServiceEnabled->isChecked());
appConfig().setMinimizeOnClose(m_pCheckBoxMinimizeOnClose->isChecked());
appConfig().saveSettings(); appConfig().saveSettings();
QDialog::accept(); QDialog::accept();
@ -127,18 +132,17 @@ void SettingsDialog::loadFromConfig() {
m_pComboLogLevel->setCurrentIndex(appConfig().logLevel()); m_pComboLogLevel->setCurrentIndex(appConfig().logLevel());
m_pCheckBoxLogToFile->setChecked(appConfig().logToFile()); m_pCheckBoxLogToFile->setChecked(appConfig().logToFile());
m_pLineEditLogFilename->setText(appConfig().logFilename()); m_pLineEditLogFilename->setText(appConfig().logFilename());
m_pCheckBoxAutoHide->setChecked(appConfig().getAutoHide()); m_pCheckBoxAutoHide->setChecked(appConfig().autoHide());
m_pCheckBoxPreventSleep->setChecked(appConfig().getPreventSleep()); m_pCheckBoxPreventSleep->setChecked(appConfig().preventSleep());
m_pCheckBoxMinimizeToTray->setChecked(appConfig().getMinimizeToTray()); m_pCheckBoxMinimizeToTray->setChecked(appConfig().minimizeToTray());
m_pLineEditCertificatePath->setText(appConfig().getTLSCertPath()); m_pLineEditCertificatePath->setText(appConfig().tlsCertPath());
m_pCheckBoxEnableCrypto->setChecked(m_appConfig.getCryptoEnabled()); m_pCheckBoxEnableCrypto->setChecked(m_appConfig.cryptoEnabled());
m_pCheckBoxLanguageSync->setChecked(m_appConfig.getLanguageSync()); m_pCheckBoxLanguageSync->setChecked(m_appConfig.languageSync());
m_pCheckBoxScrollDirection->setChecked( m_pCheckBoxScrollDirection->setChecked(m_appConfig.invertScrollDirection());
m_appConfig.getInvertScrollDirection()); m_pCheckBoxClientHostMode->setChecked(m_appConfig.clientHostMode());
m_pCheckBoxClientHostMode->setChecked(m_appConfig.getClientHostMode()); m_pCheckBoxServerClientMode->setChecked(m_appConfig.serverClientMode());
m_pCheckBoxServerClientMode->setChecked(m_appConfig.getServerClientMode()); m_pCheckBoxServiceEnabled->setChecked(m_appConfig.serviceEnabled());
m_pCheckBoxMinimizeOnClose->setChecked(m_appConfig.minimizeOnClose());
setupSeurity();
if (m_appConfig.isSystemScoped()) { if (m_appConfig.isSystemScoped()) {
m_pRadioSystemScope->setChecked(true); m_pRadioSystemScope->setChecked(true);
@ -146,44 +150,42 @@ void SettingsDialog::loadFromConfig() {
m_pRadioUserScope->setChecked(true); m_pRadioUserScope->setChecked(true);
} }
#if defined(Q_OS_WIN) updateTlsControls();
m_pComboElevate->setCurrentIndex(static_cast<int>(appConfig().elevateMode()));
#else
// elevate checkbox is only useful on ms windows.
m_pLabelElevate->hide();
m_pComboElevate->hide();
#endif
m_pCheckBoxClientHostMode->setVisible(
isClientMode() && appConfig().getInitiateConnectionFromServer());
m_pCheckBoxServerClientMode->setVisible(
!isClientMode() && appConfig().getInitiateConnectionFromServer());
} }
void SettingsDialog::setupSeurity() { void SettingsDialog::updateTlsControls() {
// If the tls file exists test its key length if (QFile(appConfig().tlsCertPath()).exists()) {
if (QFile(appConfig().getTLSCertPath()).exists()) { updateKeyLengthOnFile(appConfig().tlsCertPath());
updateKeyLengthOnFile(appConfig().getTLSCertPath());
} else { } else {
m_pComboBoxKeyLength->setCurrentIndex( m_pComboBoxKeyLength->setCurrentIndex(
m_pComboBoxKeyLength->findText(appConfig().getTLSKeyLength())); m_pComboBoxKeyLength->findText(appConfig().tlsKeyLength()));
} }
m_pCheckBoxEnableCrypto->setChecked(m_appConfig.getCryptoEnabled()); m_pCheckBoxEnableCrypto->setChecked(m_appConfig.cryptoEnabled());
if (appConfig().getClientGroupChecked()) { updateTlsControlsEnabled();
m_pLabelKeyLength->hide();
m_pComboBoxKeyLength->hide();
m_pLabelCertificate->hide();
m_pLineEditCertificatePath->hide();
m_pPushButtonBrowseCert->hide();
m_pPushButtonRegenCert->hide();
} }
void SettingsDialog::updateTlsControlsEnabled() {
auto clientMode = appConfig().clientGroupChecked();
auto cryptoAvailable = appConfig().cryptoAvailable();
auto tlsChecked = m_pCheckBoxEnableCrypto->isChecked();
auto enabled = !clientMode && cryptoAvailable && tlsChecked;
qDebug(
"TLS controls enabled=%d, client=%d, crypto=%d, checked=%d", enabled,
clientMode, cryptoAvailable, tlsChecked);
m_pLabelKeyLength->setEnabled(enabled);
m_pComboBoxKeyLength->setEnabled(enabled);
m_pLabelCertificate->setEnabled(enabled);
m_pLineEditCertificatePath->setEnabled(enabled);
m_pPushButtonBrowseCert->setEnabled(enabled);
m_pPushButtonRegenCert->setEnabled(enabled);
} }
bool SettingsDialog::isClientMode() const { bool SettingsDialog::isClientMode() const {
return (m_pMainWindow->synergyType() == MainWindow::synergyClient); return (m_pMainWindow->coreMode() == MainWindow::CoreMode::Client);
} }
void SettingsDialog::on_m_pCheckBoxLogToFile_stateChanged(int i) { void SettingsDialog::on_m_pCheckBoxLogToFile_stateChanged(int i) {
@ -192,7 +194,6 @@ void SettingsDialog::on_m_pCheckBoxLogToFile_stateChanged(int i) {
m_pLabelLogPath->setEnabled(checked); m_pLabelLogPath->setEnabled(checked);
m_pLineEditLogFilename->setEnabled(checked); m_pLineEditLogFilename->setEnabled(checked);
m_pButtonBrowseLog->setEnabled(checked); m_pButtonBrowseLog->setEnabled(checked);
buttonBox->button(QDialogButtonBox::Save)->setEnabled(isModified());
} }
void SettingsDialog::on_m_pButtonBrowseLog_clicked() { void SettingsDialog::on_m_pButtonBrowseLog_clicked() {
@ -206,17 +207,9 @@ void SettingsDialog::on_m_pButtonBrowseLog_clicked() {
} }
void SettingsDialog::on_m_pCheckBoxEnableCrypto_clicked(bool checked) { void SettingsDialog::on_m_pCheckBoxEnableCrypto_clicked(bool checked) {
if (appConfig().isCryptoAvailable()) { updateTlsControlsEnabled();
m_pLabelKeyLength->setEnabled(checked);
m_pComboBoxKeyLength->setEnabled(checked);
m_pLabelCertificate->setEnabled(checked);
m_pLineEditCertificatePath->setEnabled(checked);
m_pPushButtonBrowseCert->setEnabled(checked);
m_pPushButtonRegenCert->setEnabled(checked);
buttonBox->button(QDialogButtonBox::Save)->setEnabled(isModified()); if (!appConfig().cryptoAvailable()) {
} else {
m_pCheckBoxEnableCrypto->setChecked(false);
#ifdef SYNERGY_ENABLE_LICENSING #ifdef SYNERGY_ENABLE_LICENSING
auto edition = appConfig().edition(); auto edition = appConfig().edition();
@ -238,9 +231,7 @@ void SettingsDialog::on_m_pRadioSystemScope_toggled(bool checked) {
// We only need to test the System scoped Radio as they are connected // We only need to test the System scoped Radio as they are connected
appConfig().setLoadFromSystemScope(checked); appConfig().setLoadFromSystemScope(checked);
loadFromConfig(); loadFromConfig();
buttonBox->button(QDialogButtonBox::Save) updateControlsEnabled();
->setEnabled(m_isSystemAtStart != checked);
enableControls(appConfig().isWritable());
} }
void SettingsDialog::on_m_pPushButtonBrowseCert_clicked() { void SettingsDialog::on_m_pPushButtonBrowseCert_clicked() {
@ -252,24 +243,23 @@ 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 the tls file exists test its key length and update
if (QFile(appConfig().getTLSCertPath()).exists()) { if (QFile(appConfig().tlsCertPath()).exists()) {
updateKeyLengthOnFile(fileName); updateKeyLengthOnFile(fileName);
} }
} }
updateRegenButton(); updateTlsRegenerateButton();
} }
void SettingsDialog::on_m_pComboBoxKeyLength_currentIndexChanged(int index) { void SettingsDialog::on_m_pComboBoxKeyLength_currentIndexChanged(int index) {
buttonBox->button(QDialogButtonBox::Save)->setEnabled(isModified()); updateTlsRegenerateButton();
updateRegenButton();
} }
void SettingsDialog::updateRegenButton() { void SettingsDialog::updateTlsRegenerateButton() {
// Disable the Regenerate cert button if the key length is different to saved // Disable the Regenerate cert button if the key length is different to saved
auto keyChanged = auto keyChanged =
appConfig().getTLSKeyLength() != m_pComboBoxKeyLength->currentText(); appConfig().tlsKeyLength() != m_pComboBoxKeyLength->currentText();
auto pathChanged = auto pathChanged =
appConfig().getTLSCertPath() != 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
// will be done on save // will be done on save
auto nor = !(keyChanged || pathChanged); auto nor = !(keyChanged || pathChanged);
@ -287,82 +277,65 @@ void SettingsDialog::updateKeyLengthOnFile(const QString &path) {
auto index = m_pComboBoxKeyLength->findText(length); auto index = m_pComboBoxKeyLength->findText(length);
m_pComboBoxKeyLength->setCurrentIndex(index); m_pComboBoxKeyLength->setCurrentIndex(index);
// Also update what is in the appconfig to match the file itself // Also update what is in the appconfig to match the file itself
appConfig().setTLSKeyLength(length); appConfig().setTlsKeyLength(length);
} }
bool SettingsDialog::isModified() { void SettingsDialog::updateControlsEnabled() {
return ( bool writable = appConfig().isWritable();
!m_pLineEditScreenName->text().isEmpty() &&
m_pLabelNameError->text().isEmpty() && m_pLineEditScreenName->setEnabled(writable);
(appConfig().screenName() != m_pLineEditScreenName->text() || m_pSpinBoxPort->setEnabled(writable);
appConfig().port() != m_pSpinBoxPort->value() || m_pLineEditInterface->setEnabled(writable);
appConfig().networkInterface() != m_pLineEditInterface->text() || m_pComboLogLevel->setEnabled(writable);
appConfig().logLevel() != m_pComboLogLevel->currentIndex() || m_pCheckBoxLogToFile->setEnabled(writable);
appConfig().logToFile() != m_pCheckBoxLogToFile->isChecked() || m_pComboElevate->setEnabled(writable);
appConfig().logFilename() != m_pLineEditLogFilename->text() || m_pCheckBoxAutoHide->setEnabled(writable);
appConfig().elevateMode() != m_pCheckBoxPreventSleep->setEnabled(writable);
static_cast<ElevateMode>(m_pComboElevate->currentIndex()) || m_pCheckBoxMinimizeToTray->setEnabled(writable);
appConfig().getAutoHide() != m_pCheckBoxAutoHide->isChecked() || m_pLineEditCertificatePath->setEnabled(writable);
appConfig().getPreventSleep() != m_pCheckBoxPreventSleep->isChecked() || m_pComboBoxKeyLength->setEnabled(writable);
appConfig().getMinimizeToTray() != m_pPushButtonBrowseCert->setEnabled(writable);
m_pCheckBoxMinimizeToTray->isChecked() || m_pCheckBoxEnableCrypto->setEnabled(writable);
appConfig().getTLSCertPath() != m_pLineEditCertificatePath->text() || m_pCheckBoxClientHostMode->setEnabled(writable);
appConfig().getTLSKeyLength() != m_pComboBoxKeyLength->currentText() || m_pCheckBoxServerClientMode->setEnabled(writable);
appConfig().getCryptoEnabled() != m_pCheckBoxEnableCrypto->isChecked() || m_pCheckBoxServiceEnabled->setEnabled(writable);
appConfig().isSystemScoped() != m_isSystemAtStart || m_pCheckBoxMinimizeOnClose->setEnabled(writable);
appConfig().getLanguageSync() != m_pCheckBoxLanguageSync->isChecked() ||
appConfig().getClientHostMode() != m_pCheckBoxLanguageSync->setEnabled(writable && isClientMode());
m_pCheckBoxClientHostMode->isChecked() || m_pCheckBoxScrollDirection->setEnabled(writable && isClientMode());
appConfig().getServerClientMode() !=
m_pCheckBoxServerClientMode->isChecked() || #if !defined(Q_OS_WIN)
appConfig().getInvertScrollDirection() != m_pCheckBoxServiceEnabled->setEnabled(false);
m_pCheckBoxScrollDirection->isChecked())); #endif
m_pCheckBoxClientHostMode->setEnabled(
writable && isClientMode() && appConfig().initiateConnectionFromServer());
m_pCheckBoxServerClientMode->setEnabled(
writable && !isClientMode() &&
appConfig().initiateConnectionFromServer());
m_pLabelLogPath->setEnabled(writable && m_pCheckBoxLogToFile->isChecked());
m_pLineEditLogFilename->setEnabled(
writable && m_pCheckBoxLogToFile->isChecked());
m_pButtonBrowseLog->setEnabled(writable && m_pCheckBoxLogToFile->isChecked());
updateTlsControlsEnabled();
if (writable) {
updateTlsRegenerateButton();
} }
void SettingsDialog::enableControls(bool enable) { #if defined(Q_OS_WIN)
m_pLineEditScreenName->setEnabled(enable); m_pComboElevate->setCurrentIndex(static_cast<int>(appConfig().elevateMode()));
m_pSpinBoxPort->setEnabled(enable); #else
m_pLineEditInterface->setEnabled(enable); // elevate checkbox is only usable on ms windows.
m_pComboLogLevel->setEnabled(enable); m_pLabelElevate->setEnabled(false);
m_pCheckBoxLogToFile->setEnabled(enable); m_pComboElevate->setEnabled(false);
m_pComboElevate->setEnabled(enable); #endif
m_pCheckBoxAutoHide->setEnabled(enable);
m_pCheckBoxPreventSleep->setEnabled(enable);
m_pCheckBoxMinimizeToTray->setEnabled(enable);
m_pLineEditCertificatePath->setEnabled(enable);
m_pComboBoxKeyLength->setEnabled(enable);
m_pPushButtonBrowseCert->setEnabled(enable);
m_pCheckBoxEnableCrypto->setEnabled(enable);
m_labelAdminRightsMessage->setVisible(!enable);
m_pCheckBoxLanguageSync->setEnabled(enable);
m_pCheckBoxScrollDirection->setEnabled(enable);
m_pCheckBoxClientHostMode->setEnabled(enable);
m_pCheckBoxServerClientMode->setEnabled(enable);
if (enable) { updateTlsControls();
m_pLabelLogPath->setEnabled(m_pCheckBoxLogToFile->isChecked());
m_pLineEditLogFilename->setEnabled(m_pCheckBoxLogToFile->isChecked());
m_pButtonBrowseLog->setEnabled(m_pCheckBoxLogToFile->isChecked());
m_pLabelKeyLength->setEnabled(m_pCheckBoxEnableCrypto->isChecked());
m_pComboBoxKeyLength->setEnabled(m_pCheckBoxEnableCrypto->isChecked());
m_pLabelCertificate->setEnabled(m_pCheckBoxEnableCrypto->isChecked());
m_pLineEditCertificatePath->setEnabled(
m_pCheckBoxEnableCrypto->isChecked());
m_pPushButtonBrowseCert->setEnabled(m_pCheckBoxEnableCrypto->isChecked());
updateRegenButton();
} else {
m_pLabelLogPath->setEnabled(enable);
m_pLineEditLogFilename->setEnabled(enable);
m_pButtonBrowseLog->setEnabled(enable);
m_pLabelKeyLength->setEnabled(enable);
m_pComboBoxKeyLength->setEnabled(enable);
m_pLabelCertificate->setEnabled(enable);
m_pLineEditCertificatePath->setEnabled(enable);
m_pPushButtonBrowseCert->setEnabled(enable);
m_pPushButtonRegenCert->setEnabled(enable);
}
} }
void SettingsDialog::onChange() { void SettingsDialog::on_m_pScreenNameValidator_finished(const QString &error) {
buttonBox->button(QDialogButtonBox::Save)->setEnabled(isModified()); m_nameError = error;
} }

View file

@ -1,6 +1,6 @@
/* /*
* synergy -- mouse and keyboard sharing utility * synergy -- mouse and keyboard sharing utility
* Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2012 Symless Ltd.
* Copyright (C) 2008 Volker Lanz (vl@fidra.de) * 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
@ -16,15 +16,15 @@
* 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(SETTINGSDIALOG_H) #pragma once
#define SETTINGSDIALOG_H
#include "ui_SettingsDialogBase.h" #include "ui_SettingsDialogBase.h"
#include <QDialog>
#include <memory>
#include "CoreInterface.h" #include "CoreInterface.h"
#include "validators/ScreenNameValidator.h"
#include <QDialog>
#include <memory>
class MainWindow; class MainWindow;
class AppConfig; class AppConfig;
@ -35,49 +35,46 @@ class SettingsDialog : public QDialog, public Ui::SettingsDialogBase {
public: public:
SettingsDialog(QWidget *parent, AppConfig &config); SettingsDialog(QWidget *parent, AppConfig &config);
static QString browseForSynergyc( static QString browseForSynergyc(
QWidget *parent, const QString &programDir, const QString &synergycName); QWidget *parent, const QString &programDir,
const QString &coreClientName);
static QString browseForSynergys( static QString browseForSynergys(
QWidget *parent, const QString &programDir, const QString &synergysName); QWidget *parent, const QString &programDir,
const QString &coreServerName);
protected: protected:
void accept() override; void accept() override;
void reject() override; void reject() override;
AppConfig &appConfig() { return m_appConfig; } AppConfig &appConfig() { return m_appConfig; }
/// @brief Causes the dialog to load all the settings from m_appConfig /// @brief Load all settings.
void loadFromConfig(); void loadFromConfig();
/// @brief Check if the regenerate button should be enabled or disabled and /// @brief Enables or disables the TLS regenerate button.
/// sets it void updateTlsRegenerateButton();
void updateRegenButton();
/// @brief Updates the key length value based on the loaded file /// @brief Updates the key length value based on the loaded file.
/// @param [in] QString path The path to the file to test
void updateKeyLengthOnFile(const QString &path); void updateKeyLengthOnFile(const QString &path);
/// @brief Check if there are modifications. /// @brief Enables controls when they should be.
/// @return true if there are modifications. void updateControlsEnabled();
bool isModified();
/// @brief Enables\disables all controls.
void enableControls(bool enabled);
/// @brief This method setups security section in setting
void setupSeurity();
/// @brief Returns true if current mode is a client mode
bool isClientMode() const; bool isClientMode() const;
void updateTlsControls();
void updateTlsControlsEnabled();
private: private:
MainWindow *m_pMainWindow; MainWindow *m_pMainWindow;
AppConfig &m_appConfig; AppConfig &m_appConfig;
CoreInterface m_CoreInterface; CoreInterface m_CoreInterface;
std::unique_ptr<validators::ScreenNameValidator> m_screenNameValidator;
/// @brief Stores settings scope at start of settings dialog /// @brief Stores settings scope at start of settings dialog
/// This is neccessary to restore state if user changes /// This is neccessary to restore state if user changes
/// the scope and doesn't save changes /// the scope and doesn't save changes
bool m_isSystemAtStart = false; bool m_isSystemAtStart = false;
QString m_nameError = "";
private slots: private slots:
void on_m_pCheckBoxEnableCrypto_clicked(bool checked); void on_m_pCheckBoxEnableCrypto_clicked(bool checked);
void on_m_pCheckBoxLogToFile_stateChanged(int); void on_m_pCheckBoxLogToFile_stateChanged(int);
@ -100,8 +97,5 @@ private slots:
/// haven't changed /// haven't changed
void on_m_pPushButtonRegenCert_clicked(); void on_m_pPushButtonRegenCert_clicked();
/// @brief This slot handles common functionality for all fields. void on_m_pScreenNameValidator_finished(const QString &error);
void onChange();
}; };
#endif

View file

@ -6,8 +6,8 @@
<rect> <rect>
<x>0</x> <x>0</x>
<y>0</y> <y>0</y>
<width>404</width> <width>417</width>
<height>819</height> <height>786</height>
</rect> </rect>
</property> </property>
<property name="sizePolicy"> <property name="sizePolicy">
@ -38,64 +38,6 @@
<property name="verticalSpacing"> <property name="verticalSpacing">
<number>5</number> <number>5</number>
</property> </property>
<item row="2" column="0">
<widget class="QLabel" name="m_pLabelGroupNetwork">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Security</string>
</property>
</widget>
</item>
<item row="8" column="0">
<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>3</height>
</size>
</property>
</spacer>
</item>
<item row="12" column="0">
<widget class="QLabel" name="m_pLabelGroupAdvanced">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Advanced</string>
</property>
</widget>
</item>
<item row="4" column="0">
<spacer name="verticalSpacer_4">
<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>3</height>
</size>
</property>
</spacer>
</item>
<item row="6" column="0"> <item row="6" column="0">
<widget class="QFrame" name="m_pGroupLog1"> <widget class="QFrame" name="m_pGroupLog1">
<property name="styleSheet"> <property name="styleSheet">
@ -181,7 +123,7 @@ background-color: rgba(192,192,192, 0.1);
</size> </size>
</property> </property>
<property name="text"> <property name="text">
<string>&amp;Level</string> <string>Level</string>
</property> </property>
<property name="buddy"> <property name="buddy">
<cstring>m_pComboLogLevel</cstring> <cstring>m_pComboLogLevel</cstring>
@ -285,8 +227,88 @@ background-color: rgba(192,192,192, 0.1);
</property> </property>
</widget> </widget>
</item> </item>
<item row="5" column="0"> <item row="0" column="0">
<widget class="QLabel" name="m_pLabelGroupLog"> <widget class="QFrame" name="m_pGroupBoxCommon">
<property name="styleSheet">
<string notr="true">.QFrame{
border: 1px solid rgba(192,192,192, 0.2);
border-radius: 4px;
background-color: rgba(192,192,192, 0.1);
}
</string>
</property>
<property name="frameShape">
<enum>QFrame::StyledPanel</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Raised</enum>
</property>
<layout class="QVBoxLayout" name="verticalLayout_3">
<property name="topMargin">
<number>17</number>
</property>
<property name="bottomMargin">
<number>17</number>
</property>
<item>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QLabel" name="m_pLabelComputerName">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>75</width>
<height>0</height>
</size>
</property>
<property name="text">
<string>Computer &amp;name</string>
</property>
<property name="buddy">
<cstring>m_pLineEditScreenName</cstring>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeType">
<enum>QSizePolicy::Maximum</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QLineEdit" name="m_pLineEditScreenName">
<property name="enabled">
<bool>true</bool>
</property>
<property name="maxLength">
<number>255</number>
</property>
</widget>
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_2">
<property name="topMargin">
<number>0</number>
</property>
<item>
<widget class="QLabel" name="m_pLabel_20">
<property name="sizePolicy"> <property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Fixed"> <sizepolicy hsizetype="Preferred" vsizetype="Fixed">
<horstretch>0</horstretch> <horstretch>0</horstretch>
@ -294,12 +316,137 @@ background-color: rgba(192,192,192, 0.1);
</sizepolicy> </sizepolicy>
</property> </property>
<property name="text"> <property name="text">
<string>Logs</string> <string>P&amp;ort</string>
</property>
<property name="buddy">
<cstring>m_pSpinBoxPort</cstring>
</property> </property>
</widget> </widget>
</item> </item>
<item row="11" column="0"> <item>
<spacer name="verticalSpacer_3"> <widget class="QSpinBox" name="m_pSpinBoxPort">
<property name="enabled">
<bool>true</bool>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="MinimumExpanding" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="maximum">
<number>65535</number>
</property>
<property name="value">
<number>24800</number>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="m_pLabel_21">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Network IP</string>
</property>
<property name="buddy">
<cstring>m_pLineEditInterface</cstring>
</property>
</widget>
</item>
<item>
<widget class="QLineEdit" name="m_pLineEditInterface">
<property name="enabled">
<bool>true</bool>
</property>
</widget>
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_3">
<property name="topMargin">
<number>0</number>
</property>
<item>
<widget class="QCheckBox" name="m_pCheckBoxMinimizeOnClose">
<property name="text">
<string>Minimize to tray on close</string>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_6">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QCheckBox" name="m_pCheckBoxMinimizeToTray">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>0</width>
<height>0</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>16777215</width>
<height>16777215</height>
</size>
</property>
<property name="layoutDirection">
<enum>Qt::LeftToRight</enum>
</property>
<property name="text">
<string>Minimize to system &amp;tray</string>
</property>
</widget>
</item>
</layout>
</item>
<item>
<widget class="QCheckBox" name="m_pCheckBoxAutoHide">
<property name="text">
<string>Hide window on start</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item row="2" column="0">
<widget class="QLabel" name="m_pLabelGroupNetwork">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Security</string>
</property>
</widget>
</item>
<item row="8" column="0">
<spacer name="verticalSpacer_5">
<property name="orientation"> <property name="orientation">
<enum>Qt::Vertical</enum> <enum>Qt::Vertical</enum>
</property> </property>
@ -337,35 +484,56 @@ background-color: rgba(192,192,192, 0.1);
<enum>QFrame::Raised</enum> <enum>QFrame::Raised</enum>
</property> </property>
<layout class="QGridLayout" name="gridLayout_3"> <layout class="QGridLayout" name="gridLayout_3">
<item row="1" column="0" colspan="3"> <item row="7" column="0" colspan="3">
<widget class="QCheckBox" name="m_pCheckBoxScrollDirection"> <widget class="QCheckBox" name="m_pCheckBoxScrollDirection">
<property name="text"> <property name="text">
<string>Invert scroll direction on this computer</string> <string>Invert scroll direction on this computer (client mode)</string>
</property> </property>
</widget> </widget>
</item> </item>
<item row="7" column="0"> <item row="8" column="0" colspan="3">
<spacer name="verticalSpacer_6"> <widget class="QCheckBox" name="m_pCheckBoxLanguageSync">
<property name="orientation"> <property name="text">
<enum>Qt::Vertical</enum> <string>Use server's keyboard language on this computer (client mode)</string>
</property> </property>
<property name="sizeHint" stdset="0"> </widget>
</item>
<item row="2" column="0">
<widget class="QCheckBox" name="m_pCheckBoxServiceEnabled">
<property name="text">
<string>Use background service (daemon)</string>
</property>
</widget>
</item>
<item row="3" column="0" rowspan="3" colspan="2">
<layout class="QHBoxLayout" name="horizontalLayout_9">
<item>
<widget class="QLabel" name="m_pLabelElevate">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Elevate privileges (service required)</string>
</property>
</widget>
</item>
<item>
<widget class="QComboBox" name="m_pComboElevate">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size> <size>
<width>20</width> <width>0</width>
<height>40</height> <height>0</height>
</size> </size>
</property> </property>
</spacer>
</item>
<item row="4" column="0">
<widget class="QCheckBox" name="m_pCheckBoxClientHostMode">
<property name="text">
<string>Host mode</string>
</property>
</widget>
</item>
<item row="5" column="1">
<widget class="QComboBox" name="m_pComboElevate">
<property name="toolTip"> <property name="toolTip">
<string>Specify when the Synergy service should run at an elevated privilege level</string> <string>Specify when the Synergy service should run at an elevated privilege level</string>
</property> </property>
@ -389,51 +557,51 @@ background-color: rgba(192,192,192, 0.1);
</item> </item>
</widget> </widget>
</item> </item>
<item row="2" column="0" colspan="3"> </layout>
<widget class="QCheckBox" name="m_pCheckBoxLanguageSync"> </item>
<item row="1" column="0">
<widget class="QCheckBox" name="m_pCheckBoxPreventSleep">
<property name="text"> <property name="text">
<string>&amp;Use server's keyboard language on this machine</string> <string>Stop this computer from sleeping</string>
</property> </property>
</widget> </widget>
</item> </item>
<item row="6" column="0"> <item row="9" column="0" colspan="3">
<layout class="QHBoxLayout" name="horizontalLayout_8">
<property name="sizeConstraint">
<enum>QLayout::SetNoConstraint</enum>
</property>
<item>
<widget class="QCheckBox" name="m_pCheckBoxServerClientMode"> <widget class="QCheckBox" name="m_pCheckBoxServerClientMode">
<property name="text"> <property name="text">
<string>Client mode</string> <string>Use server as a client</string>
</property> </property>
</widget> </widget>
</item> </item>
<item row="5" column="0"> <item>
<widget class="QLabel" name="m_pLabelElevate"> <spacer name="horizontalSpacer_8">
<property name="sizePolicy"> <property name="orientation">
<sizepolicy hsizetype="Preferred" vsizetype="Fixed"> <enum>Qt::Horizontal</enum>
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property> </property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QCheckBox" name="m_pCheckBoxClientHostMode">
<property name="text"> <property name="text">
<string>Elevate privileges</string> <string>Use client in host mode</string>
</property> </property>
</widget> </widget>
</item> </item>
</layout> </layout>
</widget>
</item> </item>
<item row="1" column="0"> </layout>
<spacer name="verticalSpacer_2"> </widget>
<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>3</height>
</size>
</property>
</spacer>
</item> </item>
<item row="3" column="0"> <item row="3" column="0">
<widget class="QFrame" name="m_pGroupSecurity"> <widget class="QFrame" name="m_pGroupSecurity">
@ -472,7 +640,7 @@ background-color: rgba(192,192,192, 0.1);
<bool>true</bool> <bool>true</bool>
</property> </property>
<property name="text"> <property name="text">
<string>Enable &amp;TLS encryption</string> <string>Enable TLS encryption</string>
</property> </property>
</widget> </widget>
</item> </item>
@ -628,6 +796,61 @@ background-color: rgba(192,192,192, 0.1);
</layout> </layout>
</widget> </widget>
</item> </item>
<item row="12" column="0">
<widget class="QLabel" name="m_pLabelGroupAdvanced">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Advanced</string>
</property>
</widget>
</item>
<item row="14" column="0">
<spacer name="verticalSpacer_6">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
<item row="5" column="0">
<widget class="QLabel" name="m_pLabelGroupLog">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Logs</string>
</property>
</widget>
</item>
<item row="4" column="0">
<spacer name="verticalSpacer_4">
<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>3</height>
</size>
</property>
</spacer>
</item>
<item row="10" column="0"> <item row="10" column="0">
<widget class="QFrame" name="m_pGroupScope"> <widget class="QFrame" name="m_pGroupScope">
<property name="styleSheet"> <property name="styleSheet">
@ -654,13 +877,6 @@ background-color: rgba(192,192,192, 0.1);
<property name="verticalSpacing"> <property name="verticalSpacing">
<number>6</number> <number>6</number>
</property> </property>
<item row="0" column="0">
<widget class="QRadioButton" name="m_pRadioUserScope">
<property name="text">
<string>Current user</string>
</property>
</widget>
</item>
<item row="0" column="1"> <item row="0" column="1">
<widget class="QRadioButton" name="m_pRadioSystemScope"> <widget class="QRadioButton" name="m_pRadioSystemScope">
<property name="text"> <property name="text">
@ -671,33 +887,18 @@ background-color: rgba(192,192,192, 0.1);
</property> </property>
</widget> </widget>
</item> </item>
<item row="1" column="0" colspan="2"> <item row="0" column="0">
<widget class="QLabel" name="m_labelAdminRightsMessage"> <widget class="QRadioButton" name="m_pRadioUserScope">
<property name="enabled">
<bool>false</bool>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="font">
<font>
<weight>50</weight>
<bold>false</bold>
</font>
</property>
<property name="text"> <property name="text">
<string>Note: Only Admins can edit settings for all users.</string> <string>Current user</string>
</property> </property>
</widget> </widget>
</item> </item>
</layout> </layout>
</widget> </widget>
</item> </item>
<item row="20" column="0"> <item row="18" column="0">
<widget class="QDialogButtonBox" name="buttonBox"> <widget class="QDialogButtonBox" name="m_pButtonBox">
<property name="orientation"> <property name="orientation">
<enum>Qt::Horizontal</enum> <enum>Qt::Horizontal</enum>
</property> </property>
@ -706,56 +907,8 @@ background-color: rgba(192,192,192, 0.1);
</property> </property>
</widget> </widget>
</item> </item>
<item row="18" column="0"> <item row="11" column="0">
<layout class="QGridLayout" name="m_pGridLayoutNetwork"> <spacer name="verticalSpacer_3">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>1</number>
</property>
<property name="horizontalSpacing">
<number>0</number>
</property>
<property name="verticalSpacing">
<number>6</number>
</property>
<item row="0" column="0">
<widget class="QCheckBox" name="m_pCheckBoxAutoConfig">
<property name="enabled">
<bool>false</bool>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Ignored" vsizetype="Ignored">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Enable Auto Config</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QLabel" name="m_pLabelInstallBonjour">
<property name="sizePolicy">
<sizepolicy hsizetype="Ignored" vsizetype="Ignored">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;a href=&quot;#&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#007af4;&quot;&gt;Install Bonjour&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="textFormat">
<enum>Qt::RichText</enum>
</property>
</widget>
</item>
</layout>
</item>
<item row="19" column="0">
<spacer name="verticalSpacer">
<property name="orientation"> <property name="orientation">
<enum>Qt::Vertical</enum> <enum>Qt::Vertical</enum>
</property> </property>
@ -770,244 +923,56 @@ background-color: rgba(192,192,192, 0.1);
</property> </property>
</spacer> </spacer>
</item> </item>
<item row="0" column="0"> <item row="1" column="0">
<widget class="QFrame" name="m_pGroupBoxCommon"> <spacer name="verticalSpacer_2">
<property name="styleSheet">
<string notr="true">.QFrame{
border: 1px solid rgba(192,192,192, 0.2);
border-radius: 4px;
background-color: rgba(192,192,192, 0.1);
}
</string>
</property>
<property name="frameShape">
<enum>QFrame::StyledPanel</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Raised</enum>
</property>
<layout class="QVBoxLayout" name="verticalLayout_3">
<property name="topMargin">
<number>17</number>
</property>
<property name="bottomMargin">
<number>17</number>
</property>
<item>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QLabel" name="m_pLabelComputerName">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>75</width>
<height>0</height>
</size>
</property>
<property name="text">
<string>Computer name</string>
</property>
<property name="buddy">
<cstring>m_pLineEditScreenName</cstring>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer">
<property name="orientation"> <property name="orientation">
<enum>Qt::Horizontal</enum> <enum>Qt::Vertical</enum>
</property> </property>
<property name="sizeType"> <property name="sizeType">
<enum>QSizePolicy::Maximum</enum> <enum>QSizePolicy::Fixed</enum>
</property> </property>
<property name="sizeHint" stdset="0"> <property name="sizeHint" stdset="0">
<size> <size>
<width>20</width> <width>20</width>
<height>20</height> <height>3</height>
</size> </size>
</property> </property>
</spacer> </spacer>
</item> </item>
<item>
<widget class="QLineEdit" name="m_pLineEditScreenName">
<property name="enabled">
<bool>true</bool>
</property>
<property name="maxLength">
<number>255</number>
</property>
</widget>
</item>
</layout>
</item>
<item>
<widget class="QLabel" name="m_pLabelNameError">
<property name="sizePolicy">
<sizepolicy hsizetype="Minimum" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="styleSheet">
<string notr="true">color: #EC4C47;
font-size: 13px;
font-family: Arial;
font-weight: bold;</string>
</property>
<property name="text">
<string/>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_2">
<property name="topMargin">
<number>0</number>
</property>
<item>
<widget class="QLabel" name="m_pLabel_20">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>P&amp;ort</string>
</property>
<property name="buddy">
<cstring>m_pSpinBoxPort</cstring>
</property>
</widget>
</item>
<item>
<widget class="QSpinBox" name="m_pSpinBoxPort">
<property name="enabled">
<bool>true</bool>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="MinimumExpanding" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="maximum">
<number>65535</number>
</property>
<property name="value">
<number>24800</number>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="m_pLabel_21">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Network IP</string>
</property>
<property name="buddy">
<cstring>m_pLineEditInterface</cstring>
</property>
</widget>
</item>
<item>
<widget class="QLineEdit" name="m_pLineEditInterface">
<property name="enabled">
<bool>true</bool>
</property>
</widget>
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_3">
<property name="topMargin">
<number>0</number>
</property>
<item>
<widget class="QCheckBox" name="m_pCheckBoxAutoHide">
<property name="text">
<string>&amp;Hide on startup</string>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_6">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QCheckBox" name="m_pCheckBoxMinimizeToTray">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>0</width>
<height>0</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>16777215</width>
<height>16777215</height>
</size>
</property>
<property name="layoutDirection">
<enum>Qt::LeftToRight</enum>
</property>
<property name="text">
<string>Minimize to system &amp;tray</string>
</property>
</widget>
</item>
</layout>
</item>
<item>
<widget class="QCheckBox" name="m_pCheckBoxPreventSleep">
<property name="text">
<string>Stop this computer from sleeping</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
</layout> </layout>
</widget> </widget>
<tabstops> <tabstops>
<tabstop>m_pCheckBoxAutoConfig</tabstop> <tabstop>m_pLineEditScreenName</tabstop>
<tabstop>m_pSpinBoxPort</tabstop>
<tabstop>m_pLineEditInterface</tabstop>
<tabstop>m_pCheckBoxMinimizeOnClose</tabstop>
<tabstop>m_pCheckBoxMinimizeToTray</tabstop>
<tabstop>m_pCheckBoxAutoHide</tabstop>
<tabstop>m_pCheckBoxEnableCrypto</tabstop>
<tabstop>m_pComboBoxKeyLength</tabstop>
<tabstop>m_pLineEditCertificatePath</tabstop>
<tabstop>m_pPushButtonBrowseCert</tabstop>
<tabstop>m_pPushButtonRegenCert</tabstop>
<tabstop>m_pCheckBoxLogToFile</tabstop>
<tabstop>m_pComboLogLevel</tabstop>
<tabstop>m_pLineEditLogFilename</tabstop>
<tabstop>m_pButtonBrowseLog</tabstop>
<tabstop>m_pRadioUserScope</tabstop>
<tabstop>m_pRadioSystemScope</tabstop>
<tabstop>m_pCheckBoxPreventSleep</tabstop>
<tabstop>m_pCheckBoxServiceEnabled</tabstop>
<tabstop>m_pComboElevate</tabstop>
<tabstop>m_pCheckBoxScrollDirection</tabstop>
<tabstop>m_pCheckBoxLanguageSync</tabstop>
<tabstop>m_pCheckBoxServerClientMode</tabstop>
<tabstop>m_pCheckBoxClientHostMode</tabstop>
</tabstops> </tabstops>
<resources> <resources>
<include location="../res/Synergy.qrc"/> <include location="../res/Synergy.qrc"/>
</resources> </resources>
<connections> <connections>
<connection> <connection>
<sender>buttonBox</sender> <sender>m_pButtonBox</sender>
<signal>accepted()</signal> <signal>accepted()</signal>
<receiver>SettingsDialogBase</receiver> <receiver>SettingsDialogBase</receiver>
<slot>accept()</slot> <slot>accept()</slot>
@ -1023,7 +988,7 @@ font-weight: bold;</string>
</hints> </hints>
</connection> </connection>
<connection> <connection>
<sender>buttonBox</sender> <sender>m_pButtonBox</sender>
<signal>rejected()</signal> <signal>rejected()</signal>
<receiver>SettingsDialogBase</receiver> <receiver>SettingsDialogBase</receiver>
<slot>reject()</slot> <slot>reject()</slot>

View file

@ -23,21 +23,72 @@
#include <QDir> #include <QDir>
#include <QProcess> #include <QProcess>
static const char kCertificateKeyLength[] = // RSA Bit length (e.g. 1024/2048/4096)
"rsa:"; // RSA Bit length (e.g. 1024/2048/4096) static const char *const kCertificateKeyLength = "rsa:";
static const char kCertificateHashAlgorithm[] =
"-sha256"; // fingerprint hashing algorithm // fingerprint hashing algorithm
static const char kCertificateLifetime[] = "365"; static const char *const kCertificateHashAlgorithm = "-sha256";
static const char kCertificateSubjectInfo[] = "/CN=Synergy";
static const char kCertificateFilename[] = "Synergy.pem"; static const char *const kCertificateLifetime = "365";
static const char kSslDir[] = "SSL"; static const char *const kCertificateSubjectInfo = "/CN=Synergy";
static const char kUnixOpenSslCommand[] = "openssl"; static const char *const kCertificateFilename = "Synergy.pem";
static const char *const kSslDir = "SSL";
#if defined(Q_OS_WIN) #if defined(Q_OS_WIN)
static const char kWinOpenSslBinary[] = "OpenSSL\\openssl.exe"; static const char *const kWinOpenSslDir = "OpenSSL";
static const char kConfigFile[] = "OpenSSL\\synergy.conf"; static const char *const kWinOpenSslBinary = "openssl.exe";
static const char *const kConfigFile = "synergy.conf";
#elif defined(Q_OS_UNIX)
static const char *const kUnixOpenSslCommand = "openssl";
#endif #endif
namespace synergy::gui {
#if defined(Q_OS_WIN)
QString openSslWindowsDir() {
auto appDir = QDir(QCoreApplication::applicationDirPath());
auto openSslDir = QDir(appDir.filePath(kWinOpenSslDir));
// in production, openssl is deployed with the app.
// in development, we can use the openssl path available at compile-time.
if (!openSslDir.exists()) {
openSslDir = QDir(OPENSSL_PATH);
}
// if the path still isn't found, something is seriously wrong.
if (!openSslDir.exists()) {
qFatal() << "OpenSSL dir not found: " << openSslDir;
}
return QDir::cleanPath(openSslDir.absolutePath());
}
QString openSslWindowsBinary() {
auto dir = QDir(openSslWindowsDir());
auto path = dir.filePath(kWinOpenSslBinary);
// when installed, there is no openssl bin dir; it's installed at the base.
// in development, we use the standard dir structure for openssl (bin dir).
if (!QFile::exists(path)) {
auto binDir = QDir(dir.filePath("bin"));
path = binDir.filePath(kWinOpenSslBinary);
}
// if the path still isn't found, something is seriously wrong.
if (!QFile::exists(path)) {
qFatal() << "OpenSSL binary not found: " << path;
}
return path;
}
#endif
} // namespace synergy::gui
using namespace synergy::gui;
SslCertificate::SslCertificate(QObject *parent) : QObject(parent) { SslCertificate::SslCertificate(QObject *parent) : QObject(parent) {
m_ProfileDir = m_CoreInterface.getProfileDir(); m_ProfileDir = m_CoreInterface.getProfileDir();
if (m_ProfileDir.isEmpty()) { if (m_ProfileDir.isEmpty()) {
@ -48,17 +99,16 @@ SslCertificate::SslCertificate(QObject *parent) : QObject(parent) {
bool SslCertificate::runTool(const QStringList &args) { bool SslCertificate::runTool(const QStringList &args) {
QString program; QString program;
#if defined(Q_OS_WIN) #if defined(Q_OS_WIN)
program = QCoreApplication::applicationDirPath(); program = openSslWindowsBinary();
program.append("\\").append(kWinOpenSslBinary);
#else #else
program = kUnixOpenSslCommand; program = kUnixOpenSslCommand;
#endif #endif
QStringList environment; QStringList environment;
#if defined(Q_OS_WIN) #if defined(Q_OS_WIN)
environment << QString("OPENSSL_CONF=%1\\%2") auto openSslDir = QDir(openSslWindowsDir());
.arg(QCoreApplication::applicationDirPath()) auto config = QDir::cleanPath(openSslDir.filePath(kConfigFile));
.arg(kConfigFile); environment << QString("OPENSSL_CONF=%1").arg(config);
#endif #endif
QProcess process; QProcess process;
@ -73,8 +123,7 @@ bool SslCertificate::runTool(const QStringList &args) {
standardError = process.readAllStandardError().trimmed(); standardError = process.readAllStandardError().trimmed();
} }
int code = process.exitCode(); if (int code = process.exitCode(); !success || code != 0) {
if (!success || code != 0) {
emit error(QString("SSL tool failed: %1\n\nCode: %2\nError: %3") emit error(QString("SSL tool failed: %1\n\nCode: %2\nError: %3")
.arg(program) .arg(program)
.arg(process.exitCode()) .arg(process.exitCode())
@ -99,9 +148,7 @@ void SslCertificate::generateCertificate(
const QString pathToUse = path.isEmpty() ? filename : path; const QString pathToUse = path.isEmpty() ? filename : path;
// If path is empty use filename if (QFile file(pathToUse); !file.exists() || forceGen) {
QFile file(pathToUse);
if (!file.exists() || forceGen) {
QStringList arguments; QStringList arguments;
// self signed certificate // self signed certificate
@ -123,8 +170,7 @@ void SslCertificate::generateCertificate(
arguments.append("-newkey"); arguments.append("-newkey");
arguments.append(keySize); arguments.append(keySize);
QDir sslDir(sslDirPath); if (QDir sslDir(sslDirPath); !sslDir.exists()) {
if (!sslDir.exists()) {
sslDir.mkpath("."); sslDir.mkpath(".");
} }
@ -161,7 +207,7 @@ void SslCertificate::generateFingerprint(const QString &certificateFilename) {
} }
// find the fingerprint from the tool output // find the fingerprint from the tool output
int i = m_ToolOutput.indexOf("="); auto i = m_ToolOutput.indexOf("=");
if (i != -1) { if (i != -1) {
i++; i++;
QString fingerprint = m_ToolOutput.mid(i, m_ToolOutput.size() - i); QString fingerprint = m_ToolOutput.mid(i, m_ToolOutput.size() - i);

View file

@ -1,6 +1,6 @@
/* /*
* synergy -- mouse and keyboard sharing utility * synergy -- mouse and keyboard sharing utility
* Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2012 Symless Ltd.
* Copyright (C) 2008 Volker Lanz (vl@fidra.de) * 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
@ -35,6 +35,8 @@
#include <cstdlib> #include <cstdlib>
#endif #endif
using namespace synergy::gui;
class QThreadImpl : public QThread { class QThreadImpl : public QThread {
public: public:
static void msleep(unsigned long msecs) { QThread::msleep(msecs); } static void msleep(unsigned long msecs) { QThread::msleep(msecs); }
@ -53,10 +55,6 @@ int main(int argc, char *argv[]) {
::setenv("QT_BEARER_POLL_TIMEOUT", "-1", 1); ::setenv("QT_BEARER_POLL_TIMEOUT", "-1", 1);
#endif #endif
#if QT_VERSION >= QT_VERSION_CHECK(5, 6, 0)
QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
#endif
QCoreApplication::setOrganizationName("Synergy"); QCoreApplication::setOrganizationName("Synergy");
QCoreApplication::setOrganizationDomain("http://symless.com/"); QCoreApplication::setOrganizationDomain("http://symless.com/");
QCoreApplication::setApplicationName("Synergy"); QCoreApplication::setApplicationName("Synergy");
@ -78,17 +76,11 @@ int main(int argc, char *argv[]) {
} }
#endif #endif
#ifndef Q_OS_WIN
QApplication::setQuitOnLastWindowClosed(false);
#endif
AppConfig appConfig; AppConfig appConfig;
qRegisterMetaType<Edition>("Edition"); qRegisterMetaType<Edition>("Edition");
#ifdef SYNERGY_ENABLE_LICENSING
LicenseManager licenseManager(&appConfig);
#endif
#ifdef SYNERGY_ENABLE_LICENSING #ifdef SYNERGY_ENABLE_LICENSING
LicenseManager licenseManager(&appConfig);
MainWindow mainWindow(appConfig, licenseManager); MainWindow mainWindow(appConfig, licenseManager);
#else #else
MainWindow mainWindow(appConfig); MainWindow mainWindow(appConfig);
@ -114,7 +106,7 @@ int main(int argc, char *argv[]) {
mainWindow.open(); mainWindow.open();
} }
return app.exec(); return QSynergyApplication::exec();
} }
#if defined(Q_OS_MAC) #if defined(Q_OS_MAC)

View file

@ -33,19 +33,27 @@ void LineEditValidator::addValidator(
} }
QValidator::State LineEditValidator::validate(QString &input, int &pos) const { QValidator::State LineEditValidator::validate(QString &input, int &pos) const {
if (m_pControl) { if (!m_pControl) {
showError(""); qFatal("Validator control not set");
m_pControl->setStyleSheet(""); return Invalid;
}
QString error;
for (const auto &validator : m_Validators) { for (const auto &validator : m_Validators) {
if (!validator->validate(input)) { if (!validator->validate(input)) {
m_pControl->setStyleSheet("border: 1px solid #EC4C47"); error = validator->getMessage();
showError(validator->getMessage());
break; break;
} }
} }
if (error.isEmpty()) {
m_pControl->setStyleSheet("");
} else {
showError(error);
m_pControl->setStyleSheet("border: 1px solid #EC4C47");
} }
finished(error);
return Acceptable; return Acceptable;
} }

View file

@ -1,6 +1,6 @@
/* /*
* synergy -- mouse and keyboard sharing utility * synergy -- mouse and keyboard sharing utility
* Copyright (C) 2012-2021 Symless Ltd. * Copyright (C) 2012 Symless Ltd.
* Copyright (C) 2008 Volker Lanz (vl@fidra.de) * 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
@ -15,27 +15,31 @@
* 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/>.
*/ */
#ifndef LINEEDITVALIDATOR_H
#define LINEEDITVALIDATOR_H
#include <memory> #pragma once
#include <vector>
#include <QtWidgets/QLabel>
#include <QtWidgets/QLineEdit>
#include <qvalidator.h>
#include "IStringValidator.h" #include "IStringValidator.h"
#include <QValidator>
#include <QtWidgets/QLabel>
#include <QtWidgets/QLineEdit>
#include <memory>
#include <vector>
namespace validators { namespace validators {
class LineEditValidator : public QValidator { class LineEditValidator : public QValidator {
Q_OBJECT
public: public:
explicit LineEditValidator( explicit LineEditValidator(
QLineEdit *parent = nullptr, QLabel *errors = nullptr); QLineEdit *parent = nullptr, QLabel *errors = nullptr);
QValidator::State validate(QString &input, int &pos) const override; QValidator::State validate(QString &input, int &pos) const override;
void addValidator(std::unique_ptr<IStringValidator> validator); void addValidator(std::unique_ptr<IStringValidator> validator);
signals:
void finished(const QString &message) const;
private: private:
QLabel *m_pErrors = nullptr; QLabel *m_pErrors = nullptr;
QLineEdit *m_pControl = nullptr; QLineEdit *m_pControl = nullptr;
@ -45,5 +49,3 @@ private:
}; };
} // namespace validators } // namespace validators
#endif // LINEEDITVALIDATOR_H

View file

@ -558,9 +558,9 @@ String Config::formatInterval(const Interval &x) {
(int)(x.second * 100.0f + 0.5f)); (int)(x.second * 100.0f + 0.5f));
} }
String Config::getClientAddress() const { return m_clientAddress; } String Config::getClientAddress() const { return m_ClientAddress; }
bool Config::isClientMode() const { return (!m_clientAddress.empty()); } bool Config::isClientMode() const { return (!m_ClientAddress.empty()); }
void Config::readSection(ConfigReadContext &s) { void Config::readSection(ConfigReadContext &s) {
static const char s_section[] = "section:"; static const char s_section[] = "section:";
@ -659,7 +659,7 @@ void Config::readSectionOptions(ConfigReadContext &s) {
} else if (name == "clipboardSharingSize") { } else if (name == "clipboardSharingSize") {
addOption("", kOptionClipboardSharingSize, s.parseInt(value)); addOption("", kOptionClipboardSharingSize, s.parseInt(value));
} else if (name == "clientAddress") { } else if (name == "clientAddress") {
m_clientAddress = value; m_ClientAddress = value;
} else { } else {
handled = false; handled = false;
} }

View file

@ -479,7 +479,7 @@ private:
InputFilter m_inputFilter; InputFilter m_inputFilter;
bool m_hasLockToScreenAction; bool m_hasLockToScreenAction;
IEventQueue *m_events; IEventQueue *m_events;
String m_clientAddress; String m_ClientAddress;
}; };
//! Configuration read context //! Configuration read context

View file

@ -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/>.
*/ */
// TODO: fix randomly freezing on windows // TODO: fix test freezing only on windows
#ifndef WIN32 #ifndef WIN32
#include "MainWindow.h" #include "MainWindow.h"
@ -51,18 +51,16 @@ public:
}; };
TestMainWindow() { TestMainWindow() {
m_appConfig = std::make_shared<AppConfig>(false);
#ifdef SYNERGY_ENABLE_LICENSING #ifdef SYNERGY_ENABLE_LICENSING
m_licenseManager = std::make_shared<LicenseManager>(m_appConfig.get()); m_licenseManager = std::make_shared<LicenseManager>(&m_appConfig);
m_mainWindow = m_mainWindow =
std::make_shared<MainWindowProxy>(*m_appConfig, *m_licenseManager); std::make_shared<MainWindowProxy>(m_appConfig, *m_licenseManager);
#else #else
m_mainWindow = std::make_shared<MainWindowProxy>(*m_appConfig); m_mainWindow = std::make_shared<MainWindowProxy>(m_appConfig);
#endif #endif
} }
std::shared_ptr<AppConfig> m_appConfig; AppConfig m_appConfig;
std::shared_ptr<LicenseManager> m_licenseManager; std::shared_ptr<LicenseManager> m_licenseManager;
std::shared_ptr<MainWindowProxy> m_mainWindow; std::shared_ptr<MainWindowProxy> m_mainWindow;
}; };

View file

@ -1,6 +1,6 @@
/* /*
* synergy -- mouse and keyboard sharing utility * synergy -- mouse and keyboard sharing utility
* Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2012 Symless Ltd.
* Copyright (C) 2011 Nick Bolton * Copyright (C) 2011 Nick Bolton
* *
* This package is free software; you can redistribute it and/or * This package is free software; you can redistribute it and/or
@ -18,21 +18,19 @@
#include "arch/Arch.h" #include "arch/Arch.h"
#include "base/Log.h" #include "base/Log.h"
#include "shared/ExitTimeout.h"
#if SYSAPI_WIN32 #if SYSAPI_WIN32
#include "arch/win32/ArchMiscWindows.h" #include "arch/win32/ArchMiscWindows.h"
#endif #endif
#include <fstream>
#include <gtest/gtest.h> #include <gtest/gtest.h>
#include <iostream>
#define LOCK_TIMEOUT 30 using synergy::test::ExitTimeout;
void lock(const std::string &lockFile);
void unlock(const std::string &lockFile);
int main(int argc, char **argv) { int main(int argc, char **argv) {
ExitTimeout exitTimeout(1, "Integration tests");
#if SYSAPI_WIN32 #if SYSAPI_WIN32
// record window instance for tray icon, etc // record window instance for tray icon, etc
ArchMiscWindows::setInstanceWin32(GetModuleHandle(NULL)); ArchMiscWindows::setInstanceWin32(GetModuleHandle(NULL));
@ -44,53 +42,10 @@ int main(int argc, char **argv) {
Log log; Log log;
log.setFilter(kDEBUG2); log.setFilter(kDEBUG2);
std::string lockFile;
for (int i = 0; i < argc; i++) {
const std::string option(argv[i]);
if (option.find("--lock-file") != std::string::npos) {
lockFile = argv[i + 1];
}
}
if (!lockFile.empty()) {
lock(lockFile);
}
::testing::GTEST_FLAG(throw_on_failure) = true; ::testing::GTEST_FLAG(throw_on_failure) = true;
testing::InitGoogleTest(&argc, argv); testing::InitGoogleTest(&argc, argv);
int result = RUN_ALL_TESTS();
if (!lockFile.empty()) {
unlock(lockFile);
}
// return code 1 means the test failed. // return code 1 means the test failed.
// any other non-zero code is probably a memory error. // any other non-zero code is probably a memory error.
return result; return RUN_ALL_TESTS();
} }
void lock(const std::string &lockFile) {
double start = ARCH->time();
// keep checking until timeout is reached.
while ((ARCH->time() - start) < LOCK_TIMEOUT) {
std::ifstream is(lockFile.c_str());
bool noLock = !is;
is.close();
if (noLock) {
break;
}
// check every second if file has gone.
ARCH->sleep(1);
}
// write empty lock file.
std::ofstream os(lockFile.c_str());
os.close();
}
void unlock(const std::string &lockFile) { remove(lockFile.c_str()); }

View file

@ -0,0 +1,61 @@
/*
* 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 "ExitTimeout.h"
#include <chrono>
#include <iostream>
#include <memory>
#include <string_view>
#include <thread>
namespace synergy::test {
const auto checkMilliseconds = std::chrono::milliseconds(100);
using std::chrono::duration_cast;
using std::chrono::steady_clock;
bool timeoutReached(const steady_clock::time_point &start, const int minutes) {
auto now = steady_clock::now();
auto duration = duration_cast<std::chrono::minutes>(now - start);
return duration.count() >= minutes;
}
ExitTimeout::ExitTimeout(const int minutes, const std::string_view &name)
: m_minutes(minutes),
m_name(name),
m_thread(std::make_unique<std::thread>([this]() { run(); })) {}
ExitTimeout::~ExitTimeout() {
m_running = false;
m_thread->join();
}
void ExitTimeout::run() const {
auto start = steady_clock::now();
while (m_running) {
std::this_thread::sleep_for(checkMilliseconds);
if (timeoutReached(start, m_minutes)) {
std::cerr << m_name << " timed out after " << m_minutes << " minute(s)"
<< std::endl;
std::exit(EXIT_FAILURE);
}
}
}
} // namespace synergy::test

View file

@ -0,0 +1,47 @@
/*
* 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 <string_view>
#include <thread>
namespace synergy::test {
/**
* @brief Exits the program after a specified timeout, unless destroyed.
*
* The `std::thread` class is used instead of `std::jthread` as Apple Clang has
* no `std::jthread` support: https://en.cppreference.com/w/cpp/compiler_support
*
* TODO: Switch to regular Clang instead of Apple Clang:
* https://symless.atlassian.net/browse/S1-1754
*/
class ExitTimeout {
public:
ExitTimeout(const int minutes, const std::string_view &name);
~ExitTimeout();
void run() const;
private:
bool m_running = true;
int m_minutes = 0;
std::string_view m_name;
std::unique_ptr<std::thread> m_thread;
};
} // namespace synergy::test

View file

@ -1,6 +1,6 @@
/* /*
* synergy -- mouse and keyboard sharing utility * synergy -- mouse and keyboard sharing utility
* Copyright (C) 2012-2016 Symless Ltd. * Copyright (C) 2012 Symless Ltd.
* Copyright (C) 2011 Nick Bolton * Copyright (C) 2011 Nick Bolton
* *
* This package is free software; you can redistribute it and/or * This package is free software; you can redistribute it and/or
@ -18,15 +18,19 @@
#include "arch/Arch.h" #include "arch/Arch.h"
#include "base/Log.h" #include "base/Log.h"
#include "shared/ExitTimeout.h"
#if SYSAPI_WIN32 #if SYSAPI_WIN32
#include "arch/win32/ArchMiscWindows.h" #include "arch/win32/ArchMiscWindows.h"
#endif #endif
#include <QApplication>
#include <gtest/gtest.h> #include <gtest/gtest.h>
using synergy::test::ExitTimeout;
int main(int argc, char **argv) { int main(int argc, char **argv) {
ExitTimeout exitTimeout(1, "Integration tests");
#if SYSAPI_WIN32 #if SYSAPI_WIN32
// HACK: shouldn't be needed, but logging fails without this. // HACK: shouldn't be needed, but logging fails without this.
ArchMiscWindows::setInstanceWin32(GetModuleHandle(NULL)); ArchMiscWindows::setInstanceWin32(GetModuleHandle(NULL));